本文简要介绍rust语言中 Primitive Type never
的用法。
!
类型,也称为“never”。
!
表示永远不会解析为任何值的计算类型。例如, exit
函数 fn exit(code: i32) -> !
退出进程而不返回,因此返回 !
。
break
、 continue
和 return
表达式也有类型 !
。例如,我们可以这样写:
#![feature(never_type)]
let x: ! = {
return 123
};
虽然 let
在这里毫无意义,但它说明了 !
的含义。由于 x
从未被赋值(因为 return
从整个函数返回),所以 x
可以被赋予类型 !
。我们也可以用 panic!
或 never-ending loop
替换 return 123
,并且此代码仍然有效。
!
的更实际用法在此代码中:
let num: u32 = match get_a_number() {
Some(num) => num,
None => break,
};
两个匹配臂都必须产生类型为 u32
的值,但由于 break
根本不会产生值,我们知道它永远不会产生不是 u32
的值。这说明了 !
类型的另一种行为 - 类型为 !
的表达式将强制转换为任何其他类型。
!
和泛型
万无一失的错误
您将看到显式使用!
的主要位置是在通用代码中。考虑 FromStr
特征:
trait FromStr: Sized {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
在为 String
实现此特征时,我们需要为 Err
选择一个类型。并且由于将字符串转换为字符串永远不会导致错误,因此适当的类型是 !
。 (目前实际使用的类型是没有变体的枚举,但这只是因为 !
是在以后添加到 Rust 中的,并且将来可能会更改。)如果 Err
类型为 !
由于某种原因,我们必须调用 String::from_str
,结果将是 Result<String, !>
,我们可以像这样解包:
#![feature(exhaustive_patterns)]
use std::str::FromStr;
let Ok(s) = String::from_str("hello");
由于 Err
变体包含 !
,因此它永远不会发生。如果存在exhaustive_patterns
特征,这意味着我们可以通过使用 Ok
变体来彻底匹配 Result<T, !>
。这说明了 !
的另一种行为 - 它可用于 “delete” 来自泛型类型(如 Result
)的某些枚举变体。
无限循环
尽管Result<T, !>
对于消除错误非常有用,!
也可用于删除成功。如果我们想到Result<T, !>
因为“如果这个函数返回,它就没有错误”,我们得到一个非常直观的想法Result<!, E>
同样:如果函数返回,它已出错了。
例如,考虑一个简单的 Web 服务器的情况,可以简化为:
loop {
let (client, request) = get_request().expect("disconnected");
let response = request.process();
response.send(client);
}
目前,这并不理想,因为每当我们无法获得新连接时,我们都会感到Panics。相反,我们希望跟踪此错误,如下所示:
loop {
match get_request() {
Err(err) => break err,
Ok((client, request)) => {
let response = request.process();
response.send(client);
},
}
}
现在,当服务器断开连接时,我们会以错误退出循环而不是Panics。虽然简单地返回错误可能很直观,但我们可能希望将其包装在 Result<!, E>
中:
fn server_loop() -> Result<!, ConnectionError> {
loop {
let (client, request) = get_request()?;
let response = request.process();
response.send(client);
}
}
现在,我们可以使用 ?
而不是 match
,并且返回类型更有意义:如果循环停止,则意味着发生了错误。我们甚至不必将循环包装在 Ok
中,因为 !
会自动强制转换为 Result<!, ConnectionError>
。
!
和特征
在编写自己的特征时,只要有明显的 impl
而不是 panic!
, !
就应该有一个 impl
。原因是返回 impl Trait
的函数,其中 !
没有 Trait
的 impl
不能作为它们唯一可能的代码路径发散。换句话说,它们不能从每个代码路径返回!
。例如,此代码无法编译:
use std::ops::Add;
fn foo() -> impl Add<u32> {
unimplemented!()
}
但是这段代码确实:
use std::ops::Add;
fn foo() -> impl Add<u32> {
if true {
unimplemented!()
} else {
0
}
}
原因是,在第一个示例中,!
可以强制转换为许多可能的类型,因为许多类型实现了 Add<u32>
。但是,在第二个示例中, else
分支返回 0
,编译器从返回类型推断其类型为 u32
。由于 u32
是具体类型,因此 !
可以并且将会被强制转换为它。有关 !
这个怪癖的更多信息,请参阅问题 #36375 。
然而,事实证明,大多数特征都可以有一个 impl
用于 !
。以 Debug
为例:
#![feature(never_type)]
impl Debug for ! {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
*self
}
}
我们再次使用 !
强制转换为任何其他类型的能力,在本例中为 fmt::Result
。由于此方法将 &!
作为参数,我们知道它永远不会被调用(因为没有类型为 !
的值可供调用)。编写 *self
本质上是告诉编译器“我们知道这段代码永远无法运行,所以只需将整个函数体视为具有类型 fmt::Result
”。在为 !
实现特征时,可以大量使用此模式。通常,任何仅具有采用 self
参数的方法的特征都应该具有这样的 impl。
另一方面,不适合实施的一个特征是 Default
:
trait Default {
fn default() -> Self;
}
由于!
没有值,它也没有默认值。确实,我们可以为此编写一个简单的Panics的impl
,但对于任何类型都是如此(我们可以通过 default()
Panics来为(例如) File
impl Default
。)
相关用法
- Rust needs_drop用法及代码示例
- Rust null_mut用法及代码示例
- Rust null用法及代码示例
- Rust UdpSocket.set_multicast_loop_v6用法及代码示例
- Rust i64.overflowing_add_unsigned用法及代码示例
- Rust Box.downcast用法及代码示例
- Rust BTreeMap.last_key_value用法及代码示例
- Rust str.make_ascii_uppercase用法及代码示例
- Rust u128.checked_pow用法及代码示例
- Rust usize.wrapping_mul用法及代码示例
- Rust AtomicU8.fetch_sub用法及代码示例
- Rust PanicInfo.payload用法及代码示例
- Rust MaybeUninit.assume_init_mut用法及代码示例
- Rust String.try_reserve用法及代码示例
- Rust Mutex.new用法及代码示例
- Rust f32.exp用法及代码示例
- Rust Result.unwrap_or_else用法及代码示例
- Rust slice.sort_unstable_by_key用法及代码示例
- Rust Formatter.precision用法及代码示例
- Rust i128.log2用法及代码示例
- Rust OsStr.to_ascii_uppercase用法及代码示例
- Rust f32.hypot用法及代码示例
- Rust RefCell.try_borrow_unguarded用法及代码示例
- Rust i16.log10用法及代码示例
- Rust LowerExp用法及代码示例
注:本文由纯净天空筛选整理自rust-lang.org大神的英文原创作品 Primitive Type never。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。