铁锈的std::process::exit有这种类型
pub fn exit(code: i32) -> !其中!是 type。
为什么铁锈需要一种特殊的类型呢?
将其与System.Exit.exitWith类型所在的Haskell进行比较
exitWith :: forall a. Int -> a相应的Rust签名将是
pub fn exit<T>(code: i32) -> T对于不同的T,不需要对这个函数进行单体化,因为T从来没有实现过,所以编译应该仍然有效。
发布于 2018-08-14 07:05:21
TL;DR:因为它支持局部推理和可组合性。
将exit() -> !替换为exit<T>() -> T的想法只考虑类型系统和类型推断。你是对的,从类型推断的角度来看,两者是等价的。然而,一种语言比类型系统更有意义。
无意义码的局部推理
!的存在使得局部推理能够检测出无意义的代码。例如,考虑:
use std::process::exit;
fn main() {
exit(3);
println!("Hello, World");
}编译器立即标记println!语句:
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的签名表明它永远不会返回,因为不能创建!的任何实例,因此在它之后的任何东西都不可能被执行。
局部优化推理
类似地,rustc将有关exit签名的信息传递给LLVM优化器。
exit声明中的第一位
; std::process::exit
; Function Attrs: noreturn
declare void @_ZN3std7process4exit17hcc1d690c14e39344E(i32) unnamed_addr #5然后在使用地点,以防万一:
; 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类型的方法与使用一个库的方法是一样的。
在Rust中,!是一个一流的构造,所有库都是统一的,最好的是.即使是在不考虑!的情况下创建的库也可以正常工作。
最好的例子是Result类型(Haskell的Either)。它的完全签名是Result<T, E>,其中T是预期类型,E是错误类型。!在Result中没有什么特别之处,但它可以用!实例化。
#![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)可组合性(之二)
对不能实例化的类型进行推理的能力也扩展到不能实例化的枚举变体的推理。
例如,以下程序编译:
#![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<T, E>有两个变体:Ok(T)和Err(E),因此匹配必须考虑这两个变体。
但是,在这里,由于不能实例化!,所以Err(!)不能实例化,因此Result<T, !>只有一个变体:Ok(T)。因此,编译器只允许考虑Ok的情况。
结论
一种编程语言比它的类型系统更有意义。
编程语言是指开发人员将其意图与其他开发人员和机器通信。“永不”类型使开发人员的意图变得清晰,使其他各方能够清楚地理解开发人员的意思,而不必从偶然的线索中重构含义。
发布于 2018-08-14 04:41:34
我认为Rust需要特殊类型!的原因包括:
type Never = for<T>(T)的type Never = forall a. a。
更广泛地说,在类型别名中,不能使用类型变量(即.a)。一般参数)在RHS上,而没有在LHS上引入它们,这正是我们在这里要做的。使用空的struct/enum没有意义,因为我们这里需要一个类型别名,这样Never就可以与任何类型统一,而不是新构造的数据类型。
由于用户无法定义此类型,因此它给出了将其添加为原语的一个原因。forall a. a),编译器将需要作出任意选择w.r.t。调用约定(正如trentcl在注释中指出的那样),尽管选择并不重要。Haskell和OCaml可以避开这个问题,因为它们使用统一的内存表示。发布于 2021-06-23 01:27:10
只是做一些补充:
锈蚀中的Never是底部类型,Haskell也有它的底部类型,只是有另一个名称。
在类型理论中,数学逻辑中的一种理论,底部的类型是没有值的类型。它也被称为零或空类型,有时用up (⊥)符号表示。
最常用的语言没有一种显式表示empty type**,的方法,但它们隐式地表示了,因为代码可能会出现恐慌或无限循环。
和网站更多内容:
大多数常用的语言没有显式表示空类型的方法。有几个值得注意的例外。 由于Haskell2010,Haskell支持空数据类型。因此,它允许定义数据为空(没有构造函数)。类型空不是完全空的,因为它包含非终止程序和未定义的常量。当您想要拥有空类型时,经常使用未定义的常量,因为未定义的常量匹配任何类型(因此是所有类型的“子类型”),并且试图计算未定义的值将导致程序中止,因此它永远不会返回答案。 在Scala中,底部类型表示为Nothing。除了用于只抛出异常或不正常返回的函数之外,它还用于协变量参数化类型。例如,Scala的List是一个协变类型构造函数,因此ListNothing是所有类型A的ListA的子类型。因此Scala的Nil是标记任何类型列表结束的对象,属于ListNothing类型。 在“锈”中,底部类型称为“从不”类型,并由!表示。它存在于保证永远不返回的函数的类型签名中,例如,通过调用恐慌!()或永远循环。它也是某些控制流关键字的类型,例如中断和返回,它们不会产生值,但仍然可以作为表达式使用。 在TypeScript中,底部的类型是No.7 在带有闭包编译器注释的JavaScript中,底部类型是!Null (字面意思是Null单元类型的非空成员)。 在PHP中,底部的类型是从不。 在Python中,底部的类型是类型。
https://stackoverflow.com/questions/51832396
复制相似问题