为什么 Rust 有一个“从不"?原始类型? [英] Why does Rust have a "Never" primitive type?

查看:51
本文介绍了为什么 Rust 有一个“从不"?原始类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Rust 的 std::process::exit 有类型

Rust's std::process::exit has the type

pub fn exit(code: i32) -> !

其中 !"Never" 原始类型.

where ! is the "Never" primitive type.

为什么 Rust 需要一个特殊的类型?

Why does Rust need a special type for this?

将此与 Haskell 进行比较,其中 System.Exit.exitWith

Compare this with Haskell where the type of System.Exit.exitWith is

exitWith :: forall a. Int -> a

相应的 Rust 签名是

The corresponding Rust signature would be

pub fn exit<T>(code: i32) -> T

没有必要为不同的 T 单态这个函数,因为 T 永远不会被物化,所以编译应该仍然有效.

There is no need to monomorphize this function for different T's because a T is never materialized so compilation should still work.

推荐答案

TL;DR:因为它支持局部推理和可组合性.

你替换 exit() 的想法 ->! by exit() ->T 只考虑类型系统和类型推断.你是对的,从类型推断的角度来看,两者是等价的.然而,语言不仅仅是类型系统.

TL;DR: Because it enables local reasoning, and composability.

Your idea of replacing exit() -> ! by exit<T>() -> T only considers the type system and type inference. You are right that from a type inference point of view, both are equivalent. Yet, there is more to a language than the type system.

! 的存在允许局部推理来检测无意义的代码.例如,考虑:

The presence of ! allows local reasoning to detect nonsensical code. For example, consider:

use std::process::exit;

fn main() {
    exit(3);
    println!("Hello, World");
}

编译器立即标记println! 语句:

The compiler immediately flags the println! statement:

warning: unreachable statement
 --> src/main.rs:5:5
  |
5 |     println!("Hello, World");
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: #[warn(unreachable_code)] on by default
  = note: this error originates in a macro outside of the current crate
          (in Nightly builds, run with -Z external-macro-backtrace for more info)

怎么样?好吧,exit 的签名清楚地表明它永远不会返回,因为永远不能创建 ! 的实例,因此它之后的任何东西都不可能被执行.

How? Well, exit's signature makes it clear it will never return, since no instance of ! can ever be created, therefore anything after it cannot possibly be executed.

类似地,rustc 将有关 exit 签名的信息传递给 LLVM 优化器.

Similarly, rustc passes on this information about the signature of exit to the LLVM optimizer.

首先在exit的声明中:

; std::process::exit
; Function Attrs: noreturn
declare void @_ZN3std7process4exit17hcc1d690c14e39344E(i32) unnamed_addr #5

然后在使用站点,以防万一:

And then at the use site, just in case:

; playground::main
; Function Attrs: uwtable
define internal void @_ZN10playground4main17h9905b07d863859afE() unnamed_addr #0 !dbg !106 {
start:
; call std::process::exit
  call void @_ZN3std7process4exit17hcc1d690c14e39344E(i32 3), !dbg !108
  unreachable, !dbg !108
}

可组合性

在 C++ 中,[[noreturn]] 是一个属性.这真的很不幸,因为它没有与通用代码集成:对于有条件的 noreturn 函数,您需要经历一些麻烦,选择 noreturn 类型的方法是就像使用一个的图书馆一样多种多样.

Composability

In C++, [[noreturn]] is an attribute. This is unfortunate, really, because it does not integrate with generic code: for a conditionally noreturn function you need to go through hoops, and the ways to pick a noreturn type are as varied as there are libraries using one.

在 Rust 中,! 是一流的构造,在所有库中都是统一的,最重要的是......即使在没有 ! 的情况下创建的库也可以正常工作.

In Rust, ! is a first-class construct, uniform across all libraries, and best of all... even libraries created without ! in mind can just work.

最好的例子是 Result 类型(Haskell 的 Either).它的完整签名是 Result,其中 T 是预期类型,E 是错误类型.!Result 中没有什么特别之处,但它可以用 ! 实例化:

The best example is the Result type (Haskell's Either). Its full signature is Result<T, E> where T is the expected type and E the error type. There is nothing special about ! in Result, yet it can be instantiated with !:

#![feature(never_type)]

fn doit() -> Result<i32, !> { Ok(3) }

fn main() {
    doit().err().unwrap();
    println!("Hello, World");
}

编译器看穿了它:

warning: unreachable statement
 --> src/main.rs:7:5
  |
7 |     println!("Hello, World");
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: #[warn(unreachable_code)] on by default
  = note: this error originates in a macro outside of the current crate
          (in Nightly builds, run with -Z external-macro-backtrace for more info)

可组合性 (bis)

推理无法实例化的类型的能力还扩展到推理无法实例化的枚举变体.

Composability (bis)

The ability to reason about types that cannot be instantiated also extends to reasoning about enum variants that cannot be instantiated.

例如,以下程序编译:

#![feature(never_type, exhaustive_patterns)]

fn doit() -> Result<i32, !> {
    Ok(3)
}

fn main() {
    match doit() {
        Ok(v) => println!("{}", v),
        // No Err needed
    }

    // `Ok` is the only possible variant
    let Ok(v) = doit();
    println!("{}", v);
}

通常情况下,Result 有两个变体:Ok(T)Err(E),因此匹配必须考虑到这两种变体.

Normally, Result<T, E> has two variants: Ok(T) and Err(E), and therefore matching must account for both variants.

然而,由于 ! 不能被实例化,Err(!) 不能被实例化,因此 Result有一个变体:Ok(T).因此,编译器只允许考虑 Ok 情况.

Here, however, since ! cannot be instantiated, Err(!) cannot be, and therefore Result<T, !> has a single variant: Ok(T). The compiler therefore allows only considering the Ok case.

除了类型系统之外,编程语言还有更多内容.

There is more to a programming language than its type system.

编程语言是关于开发人员向其他开发人员和机器传达其意图.Never 类型让开发者的意图变得清晰,让其他方能够清楚地理解开发者的意思,而不必从附带的线索中重构含义.

A programming language is about a developer communicating its intent to other developers and the machine. The Never type makes the intent of the developer clear, allowing other parties to clearly understand what the developer meant, rather than having to reconstruct the meaning from incidental clues.

这篇关于为什么 Rust 有一个“从不"?原始类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆