ErrorKind :: __ Nonexhaustive的目的是什么?

时间:2016-04-06 01:45:21

标签: rust

std::io::ErrorKind有一个变体__Nonexhaustive。 如果这个变种不存在,我不知道是什么问题。

这种变体的目的是什么?

2 个答案:

答案 0 :(得分:16)

它旨在通过强制使用稳定代码中的任何ErrorKind语句来拥有一个全能match分支,从而允许将来展开_枚举。

具体来说,the variant is marked unstable等不能在稳定通道上引用,因此编译器拒绝像

这样的代码
fn foo(x: Error) {
    match x.kind() {
        ErrorKind::NotFound => {}
        ErrorKind::PermissionDenied => {}
        ErrorKind::ConnectionRefused => {}
        ErrorKind::ConnectionReset => {}
        ErrorKind::ConnectionAborted => {}
        ErrorKind::NotConnected => {}
        ErrorKind::AddrInUse => {}
        ErrorKind::AddrNotAvailable => {}
        ErrorKind::BrokenPipe => {}
        ErrorKind::AlreadyExists => {}
        ErrorKind::WouldBlock => {}
        ErrorKind::InvalidInput => {}
        ErrorKind::InvalidData => {}
        ErrorKind::TimedOut => {}
        ErrorKind::WriteZero => {}
        ErrorKind::Interrupted => {}
        ErrorKind::Other => {}
        ErrorKind::UnexpectedEof => {}
        ErrorKind::UnexpectedEOF => {}
        ErrorKind::__Nonexhaustive => {}
    }
}
<anon>:24:9: 24:35 error: use of unstable library feature 'io_error_internals': better expressed through extensible enums that this enum cannot be exhaustively matched against (see issue #0)
<anon>:24         ErrorKind::__Nonexhaustive => {}
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~

如果此代码在稳定的Rust上成功编译,那么在将来的版本中向ErrorKind添加变体会破坏任何具有上述match的代码,并且破坏稳定的代码是不好的。代码中断是因为Rust中的匹配必须是详尽的,也就是说,它们必须以某种方式涵盖所有可能性,因此添加变体意味着不包括这种可能性。

相反,程序员必须写:

fn foo(x: Error) {
    match x.kind() {
        ErrorKind::NotFound => {}
        ErrorKind::PermissionDenied => {}
        ErrorKind::ConnectionRefused => {}
        ErrorKind::ConnectionReset => {}
        ErrorKind::ConnectionAborted => {}
        ErrorKind::NotConnected => {}
        ErrorKind::AddrInUse => {}
        ErrorKind::AddrNotAvailable => {}
        ErrorKind::BrokenPipe => {}
        ErrorKind::AlreadyExists => {}
        ErrorKind::WouldBlock => {}
        ErrorKind::InvalidInput => {}
        ErrorKind::InvalidData => {}
        ErrorKind::TimedOut => {}
        ErrorKind::WriteZero => {}
        ErrorKind::Interrupted => {}
        ErrorKind::Other => {}
        ErrorKind::UnexpectedEof => {}
        ErrorKind::UnexpectedEOF => {}
        _ => {}
    }
}

这意味着将来添加到ErrorKind的任何变体(例如,新IO功能的新错误可能性)将属于_臂,因此现有的稳定代码不会中断。

答案 1 :(得分:6)

这个隐藏变体的目的是阻止你写这样的东西(由于__Nonexhaustive的存在而不能精确编译:

use std::io::ErrorKind;

fn main() {
    let error_kind: ErrorKind = unimplemented!();
    match error_kind {
        ErrorKind::NotFound => unimplemented!(),
        ErrorKind::PermissionDenied => unimplemented!(),
        ErrorKind::ConnectionRefused => unimplemented!(),
        ErrorKind::ConnectionReset => unimplemented!(),
        ErrorKind::ConnectionAborted => unimplemented!(),
        ErrorKind::NotConnected => unimplemented!(),
        ErrorKind::AddrInUse => unimplemented!(),
        ErrorKind::AddrNotAvailable => unimplemented!(),
        ErrorKind::BrokenPipe => unimplemented!(),
        ErrorKind::AlreadyExists => unimplemented!(),
        ErrorKind::WouldBlock => unimplemented!(),
        ErrorKind::InvalidInput => unimplemented!(),
        ErrorKind::InvalidData => unimplemented!(),
        ErrorKind::TimedOut => unimplemented!(),
        ErrorKind::WriteZero => unimplemented!(),
        ErrorKind::Interrupted => unimplemented!(),
        ErrorKind::Other => unimplemented!(),
        ErrorKind::UnexpectedEOF => unimplemented!(),
        ErrorKind::UnexpectedEof => unimplemented!(),
        // note: no wildcard match arm here
    };
}

为什么标准库的开发人员不希望您这样做的原因是为了保留将来向ErrorKind添加变体的功能。 __Nonexhaustive变体可以通过简单地处理每个变体来阻止您进行详尽的匹配; 必须拥有通配符才能进行详尽的匹配。

在Rust中,match表达式要求匹配的表达式的所有可能模式都具有相应的arm,因此match表达式始终具有明确定义的显式值。涵盖所有模式的match称为详尽匹配。使用enum s,Rust让我们只列出所有变体。例如,对于Option,其中只有2个变体,名为NoneSome,我们可以写:

fn main() {
    let option: Option<()> = unimplemented!();
    match option {
        None => unimplemented!(),
        Some(()) => unimplemented!(),
    };
}

这个match编译得很好,因为它涵盖了option的所有可能模式。但是,如果Option类型获得了另一个变体,那么突然您的代码将不再编译,因为它将不再是详尽无遗的。当然,这对Option没有意义,因此Option类型不会播放&#34;非穷举&#34;游戏。但如果__Nonexhaustive不存在,那么向ErrorKind添加变体将是一个重大变化;任何在ErrorKind上进行详尽匹配(没有通配符)的代码都会突然停止编译。此代码可能位于您的应用程序所依赖的包中,并且该破坏可能会阻止您升级Rust,直到 crate被修复为止!