Rust中non_exhaustive的enum
所谓non_exhaustive的enum就是定义中带有#[non_exhaustive]
的enum
,如
#[non_exhaustive]
pub enum Error {
Message(String),
Other,
}
在定义了这个enum
的crate中,non_exhaustive
没有任何效果。
let error = Error::Other;
// Non-exhaustive enums can be matched on exhaustively within the defining crate.
match error {
Error::Message(ref s) => { },
Error::Other => { },
}
但若引用的crate中存在non_exhaustive
的enum
,会发生什么呢?
以libpnet这个crate为例,在https://docs.rs/pnet/latest/pnet/#这个页面给出的示例代码中,可以看到这么一段
// Create a new channel, dealing with layer 2 packets
let (mut tx, mut rx) = match datalink::channel(&interface, Default::default()) {
Ok(Ethernet(tx, rx)) => (tx, rx),
Ok(_) => panic!("Unhandled channel type"),
Err(e) => panic!("An error occurred when creating the datalink channel: {}", e)
};
datalink::channel()
的返回值类型为Result<Channel, Error>
,而Channel
的类型是一个enum,
pub enum Channel {
Ethernet(Box<dyn DataLinkSender, Global>, Box<dyn DataLinkReceiver, Global>),
}
如果datalink::channel()
返回的是成功的结果(类型为Channel
),将与第一个Ok(Ethernet(tx, rx))
模式的前半部分匹配(因为Ok()
就对应成功的结果)。而根据Channel
的定义,Channel::Ethernet(tx, rx)
是该enum
唯一的成员(variant),所以只要是成功的结果,就应该匹配第1个模式,否则就匹配最后的Err(e)
这个模式。
这看起来满足Rust中的匹配必须是穷举式的(exhaustive)这一要求。因此似乎第2个模式Ok(_) => ,
就显得多余了。但若去掉这行,编译时就会报错Ok(_) not covered
:
error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
--> src/main.rs:33:31
|
33 | let (_tx, mut rx) = match datalink::channel(&interface, Default::default()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered
|
note: `Result<Channel, std::io::Error>` defined here
...
|
= note: not covered
= note: the matched value is of type `Result<Channel, std::io::Error>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
38 ~ },
39 + Ok(_) => todo!()
|
For more information about this error, try `rustc --explain E0004`.
这背后的原因就是因为enum Channel
是non_exhaustive
的,其完整定义是
// https://docs.rs/pnet_datalink/0.34.0/src/pnet_datalink/lib.rs.html#99
/// A channel for sending and receiving at the data link layer.
#[non_exhaustive]
pub enum Channel {
/// A datalink channel which sends and receives Ethernet packets.
Ethernet(Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>),
}
而我们又在外部引用了包含这个enum
的crate。解决的办法就是加入Ok(_) => {...}
。
感觉这应该算Rust确保程序健壮性(鲁棒性)的一方面,在编译阶段就报出潜在的错误——咱也不知道引用的crate什么时候会在enum
中增加新的成员。
参考
https://doc.rust-lang.org/beta/reference/attributes/type_syst... Type system attributes
https://docs.rs/pnet/latest/pnet/#examples This (fairly useless) code implements an Ethernet echo server.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。