pub fn exit(code: i32) -> !
其中!
是“从不”原语类型。
为什么Rust为此需要特殊的类型?
将此与Haskell进行比较,其中的类型System.Exit.exitWith
是
exitWith :: forall a. Int -> a
相应的Rust签名为
pub fn exit<T>(code: i32) -> T
无需针对不同T
的对该函数进行单态化,因为aT
从未实现,因此编译仍然可以正常进行。
您的替换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
情况。
编程语言不只是其类型系统。
编程语言是关于开发人员将其意图传达给其他开发人员和机器的信息。Never类型可以使开发人员的意图明确,让其他方可以清楚地了解开发人员的含义,而不必从偶然的线索中重新构造含义。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句