Primitive Type never

🔬This is a nightly-only experimental API. (never_type #35121)
Expand description

! 类型,也称为 “never”。

! 表示永远不会解析为任何值的计算类型。 例如,exit 函数 fn exit(code: i32) -> ! 退出该进程而不返回,因此返回 !

breakcontinuereturn 表达式也具有 ! 类型。例如,我们可以写:

#![feature(never_type)]
let x: ! = {
    return 123
};
Run

尽管 let 在这里毫无意义,但它说明了 ! 的含义。 由于从未给 x 赋值 (因为 return 从整个函数返回),因此可以给 x 指定 ! 类型。 我们也可以将 return 123 替换为 panic! 或永无休止的 loop,并且此代码仍然有效。

以下代码更实际地使用 !

let num: u32 = match get_a_number() {
    Some(num) => num,
    None => break,
};
Run

两个匹配分支都必须产生 u32 类型的值,但是由于 break 根本不会产生值,我们知道它永远不会产生不是 u32 的值。

这说明了 ! 类型的另一种行为 - 类型为 ! 的表达式将强制转换为任何其他类型。

! 和泛型

绝对的错误

您将看到显式使用的 ! 的主要位置是泛型代码。考虑 FromStr trait:

trait FromStr: Sized {
    type Err;
    fn from_str(s: &str) -> Result<Self, Self::Err>;
}
Run

当为 String 实现此 trait 时,我们需要为 Err 选择一个类型。并且由于将字符串转换为字符串永远不会导致错误,因此适当的类型是 !。 (目前实际使用的类型是一个没有变体的枚举,尽管这只是因为 ! 以后才会被添加到 Rust 中,并且将来可能会发生变化。) 对于 Err 类型的 !,如果我们由于某种原因不得不调用 String::from_str,那么结果将是 Result<String, !>,我们可以像这样解包:

#![feature(exhaustive_patterns)]
use std::str::FromStr;
let Ok(s) = String::from_str("hello");
Run

由于 Err 变体包含 !,因此永远不会发生。如果存在 exhaustive_patterns 特性,则意味着我们只需采用 Ok 变体就可以在 Result<T, !> 上进行穷尽的匹配。 这说明了 ! 的另一种行为 - 它可以用于 “delete” 泛型 (如 Result) 中的某些枚举变体。

无限循环

尽管 Result<T, !> 对于消除错误非常有用,但 ! 也可以用于消除成功。如果我们将 Result<T, !> 视为 “if this function returns, it has not errored,”,那么我们也会非常直观地想到 Result<!, E>: 如果函数返回,则 错误。

例如,考虑一个简单的 Web 服务器的情况,它可以简化为:

loop {
    let (client, request) = get_request().expect("disconnected");
    let response = request.process();
    response.send(client);
}
Run

目前,这并不理想,因为只要无法建立新的连接,我们就简单地使用 panic。 相反,我们想跟踪此错误,如下所示:

loop {
    match get_request() {
        Err(err) => break err,
        Ok((client, request)) => {
            let response = request.process();
            response.send(client);
        },
    }
}
Run

现在,当服务器断开连接时,我们以错误退出循环而不是 panic。虽然简单地返回错误可能很直观,但我们可能希望将其包装在 Result<!, E> 中:

fn server_loop() -> Result<!, ConnectionError> {
    loop {
        let (client, request) = get_request()?;
        let response = request.process();
        response.send(client);
    }
}
Run

现在,我们可以使用 ? 代替 match,并且返回类型更有意义:如果循环停止,则意味着发生了错误。我们甚至不必将循环包装在 Ok 中,因为 ! 会自动强制转换为 Result<!, ConnectionError>

! 和 traits

编写自己的 traits 时,只要有明显的 impl 而不是 panic!! 就应该有一个 impl。 原因是返回 impl Trait! 没有 impl 的函数不能作为它的唯一可能的代码路径发散。 换句话说,它们不能从每个代码路径返回 !。 例如,此代码不会编译:

use std::ops::Add;

fn foo() -> impl Add<u32> {
    unimplemented!()
}
Run

但是这段代码可以做到:

use std::ops::Add;

fn foo() -> impl Add<u32> {
    if true {
        unimplemented!()
    } else {
        0
    }
}
Run

原因是,在第一个示例中,! 可以强制转换为许多可能的类型,因为许多类型实现了 Add<u32>。 但是,在第二个示例中,else 分支返回 0,编译器从返回类型推断出它为 u32 类型。 由于 u32 是具体类型,因此 ! 可以并且将被强制使用。 有关此 ! 的更多信息,请参见问题 #36375

但是,事实证明,大多数 traits 都可以将 impl 用作 !。以 Debug 为例:

#![feature(never_type)]
impl Debug for ! {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        *self
    }
}
Run

我们再次使用 ! 的功能来强制转换为任何其他类型,在本例中为 fmt::Result。 由于此方法将 &! 作为参数,因此我们知道它永远不能被调用 (因为没有 ! 类型的值可以调用它)。 编写 *self 实质上就是告诉编译器 “我们知道这段代码永远无法运行,所以只需将整个函数体视为具有类型 fmt::Result”。 当为 ! 实现 traits 时,可以使用这种模式。 通常,任何仅具有采用 self 参数的方法的 trait 都应具有这样的含义。

另一方面,不适合实现的一个 trait 是 Default

trait Default {
    fn default() -> Self;
}
Run

由于 ! 没有值,因此也没有默认值。 的确,我们可以为此编写一个 impl,它只是 panics,但是对于任何类型都一样 (我们可以通过仅将 default() panic 制作为 (eg.) File 来使用 impl Default)。

Trait Implementations§

source§

impl Clone for !

source§

fn clone(&self) -> Self

返回值的副本。 Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

source 执行复制分配。 Read more
source§

impl Debug for !

source§

fn fmt(&self, _: &mut Formatter<'_>) -> Result

使用给定的格式化程序格式化该值。 Read more
source§

impl Display for !

source§

fn fmt(&self, _: &mut Formatter<'_>) -> Result

使用给定的格式化程序格式化该值。 Read more
source§

impl Error for !

1.30.0 · source§

fn source(&self) -> Option<&(dyn Error + 'static)>

此错误的下级来源 (如果有)。 Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, demand: &mut Demand<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access #99301)
提供对用于错误报告的上下文的基于类型的访问。 Read more
1.34.0 · source§

impl From<!> for Infallible

source§

fn from(x: !) -> Self

从输入类型转换为此类型。
source§

impl From<!> for TryFromIntError

source§

fn from(never: !) -> TryFromIntError

从输入类型转换为此类型。
1.29.0 · source§

impl Hash for !

source§

fn hash<H: Hasher>(&self, _: &mut H)

将该值输入给定的 HasherRead more
1.3.0 · source§

fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)where Self: Sized,

将这种类型的切片送入给定的 Hasher 中。 Read more
1.60.0 · source§

impl Not for !

§

type Output = !

应用 ! 运算符后的结果类型。
source§

fn not(self) -> !

执行一元 ! 操作。 Read more
source§

impl Ord for !

source§

fn cmp(&self, _: &!) -> Ordering

此方法返回 selfother 之间的 OrderingRead more
1.21.0 · source§

fn max(self, other: Self) -> Selfwhere Self: Sized,

比较并返回两个值中的最大值。 Read more
1.21.0 · source§

fn min(self, other: Self) -> Selfwhere Self: Sized,

比较并返回两个值中的最小值。 Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Selfwhere Self: Sized + PartialOrd,

将值限制在某个时间间隔内。 Read more
source§

impl PartialEq<!> for !

source§

fn eq(&self, _: &!) -> bool

此方法测试 selfother 值是否相等,并由 == 使用。
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

此方法测试 !=。 默认实现几乎总是足够的,并且不应在没有充分理由的情况下被覆盖。
source§

impl PartialOrd<!> for !

source§

fn partial_cmp(&self, _: &!) -> Option<Ordering>

如果存在,则此方法返回 selfother 值之间的顺序。 Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

此方法测试的内容少于 (对于 selfother),并且由 < 操作员使用。 Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

此方法测试小于或等于 (对于 selfother),并且由 <= 运算符使用。 Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

此方法测试大于 (对于 selfother),并且由 > 操作员使用。 Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

此方法测试是否大于或等于 (对于 selfother),并且由 >= 运算符使用。 Read more
source§

impl Copy for !

source§

impl Eq for !

Blanket Implementations§

source§

impl<T> From<!> for T

source§

fn from(t: !) -> T

从输入类型转换为此类型。