std::io::ErrorKind
有一个变体__Nonexhaustive
。
如果这个变种不存在,我不知道是什么问题。
这种变体的目的是什么?
答案 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个变体,名为None
和Some
,我们可以写:
fn main() {
let option: Option<()> = unimplemented!();
match option {
None => unimplemented!(),
Some(()) => unimplemented!(),
};
}
这个match
编译得很好,因为它涵盖了option
的所有可能模式。但是,如果Option
类型获得了另一个变体,那么突然您的代码将不再编译,因为它将不再是详尽无遗的。当然,这对Option
没有意义,因此Option
类型不会播放&#34;非穷举&#34;游戏。但如果__Nonexhaustive
不存在,那么向ErrorKind
添加变体将是一个重大变化;任何在ErrorKind
上进行详尽匹配(没有通配符)的代码都会突然停止编译。此代码可能位于您的应用程序所依赖的包中,并且该破坏可能会阻止您升级Rust,直到 crate被修复为止!