类型系统属性
以下属性用于改变类型的用法。
non_exhaustive 属性
non_exhaustive 属性表示类型或变体将来可能会添加更多字段或变体。
non_exhaustive 属性使用MetaWord 语法,因此不接受任何输入。
在定义它的 crate 内,non_exhaustive 没有效果。
#![allow(unused)] fn main() { #[non_exhaustive] pub struct Config { pub window_width: u16, pub window_height: u16, } #[non_exhaustive] pub struct Token; #[non_exhaustive] pub struct Id(pub u64); #[non_exhaustive] pub enum Error { Message(String), Other, } pub enum Message { #[non_exhaustive] Send { from: u32, to: u32, contents: String }, #[non_exhaustive] Reaction(u32), #[non_exhaustive] Quit, } // Non-exhaustive structs can be constructed as normal within the defining crate. let config = Config { window_width: 640, window_height: 480 }; let token = Token; let id = Id(4); // Non-exhaustive structs can be matched on exhaustively within the defining crate. let Config { window_width, window_height } = config; let Token = token; let Id(id_number) = id; let error = Error::Other; let message = Message::Reaction(3); // Non-exhaustive enums can be matched on exhaustively within the defining crate. match error { Error::Message(ref s) => { }, Error::Other => { }, } match message { // Non-exhaustive variants can be matched on exhaustively within the defining crate. Message::Send { from, to, contents } => { }, Message::Reaction(id) => { }, Message::Quit => { }, } }
在定义它的 crate 外,用 non_exhaustive 标注的类型具有限制,以便在添加新字段或变体时保持向后兼容性。
非穷举类型不能在定义它的 crate 外构造
- 非穷举变体(
struct或enum变体)不能使用结构体表达式(StructExpression)构造(包括使用函数式更新语法)。 - 单元结构体(unit-like struct)的隐式定义的同名常量,或元组结构体(tuple struct)的同名构造函数,其可见性不大于
pub(crate)。也就是说,如果结构体的可见性是pub,那么常量或构造函数的可见性是pub(crate);否则,两者的可见性相同(这与不使用#[non_exhaustive]时的情况相同)。 enum实例可以被构造。
以下构造示例在定义它的 crate 外无法编译
// These are types defined in an upstream crate that have been annotated as
// `#[non_exhaustive]`.
use upstream::{Config, Token, Id, Error, Message};
// Cannot construct an instance of `Config`; if new fields were added in
// a new version of `upstream` then this would fail to compile, so it is
// disallowed.
let config = Config { window_width: 640, window_height: 480 };
// Cannot construct an instance of `Token`; if new fields were added, then
// it would not be a unit-like struct any more, so the same-named constant
// created by it being a unit-like struct is not public outside the crate;
// this code fails to compile.
let token = Token;
// Cannot construct an instance of `Id`; if new fields were added, then
// its constructor function signature would change, so its constructor
// function is not public outside the crate; this code fails to compile.
let id = Id(5);
// Can construct an instance of `Error`; new variants being introduced would
// not result in this failing to compile.
let error = Error::Message("foo".to_string());
// Cannot construct an instance of `Message::Send` or `Message::Reaction`;
// if new fields were added in a new version of `upstream` then this would
// fail to compile, so it is disallowed.
let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), };
let message = Message::Reaction(0);
// Cannot construct an instance of `Message::Quit`; if this were converted to
// a tuple-variant `upstream` then this would fail to compile.
let message = Message::Quit;
在定义它的 crate 外匹配非穷举类型时存在限制
- 当对非穷举变体(
struct或enum变体)进行模式匹配时,必须使用结构体模式(StructPattern),其中必须包含..。元组变体的构造函数的可见性会降低,不大于pub(crate)。 - 当对非穷举
enum进行模式匹配时,匹配某个变体不会影响分支的穷举性。
以下匹配示例在定义它的 crate 外无法编译
// These are types defined in an upstream crate that have been annotated as
// `#[non_exhaustive]`.
use upstream::{Config, Token, Id, Error, Message};
// Cannot match on a non-exhaustive enum without including a wildcard arm.
match error {
Error::Message(ref s) => { },
Error::Other => { },
// would compile with: `_ => {},`
}
// Cannot match on a non-exhaustive struct without a wildcard.
if let Ok(Config { window_width, window_height }) = config {
// would compile with: `..`
}
// Cannot match a non-exhaustive unit-like or tuple struct except by using
// braced struct syntax with a wildcard.
// This would compile as `let Token { .. } = token;`
let Token = token;
// This would compile as `let Id { 0: id_number, .. } = id;`
let Id(id_number) = id;
match message {
// Cannot match on a non-exhaustive struct enum variant without including a wildcard.
Message::Send { from, to, contents } => { },
// Cannot match on a non-exhaustive tuple or unit enum variant.
Message::Reaction(type) => { },
Message::Quit => { },
}
也不允许对包含任何非穷举变体的 enum 使用数字类型转换(as)。
例如,以下 enum 可以进行类型转换,因为它不包含任何非穷举变体
#![allow(unused)] fn main() { #[non_exhaustive] pub enum Example { First, Second } }
然而,如果 enum 包含哪怕一个非穷举变体,类型转换都会导致错误。请考虑同一个 enum 的修改版本
#![allow(unused)] fn main() { #[non_exhaustive] pub enum EnumWithNonExhaustiveVariants { First, #[non_exhaustive] Second } }
use othercrate::EnumWithNonExhaustiveVariants;
// Error: cannot cast an enum with a non-exhaustive variant when it's defined in another crate
let _ = EnumWithNonExhaustiveVariants::First as u8;
非穷举类型在下游 crate 中总是被认为是可居住的(inhabited)。