Type system attributes
类型系统属性
type_system.md
commit: 076a798583ecb450dbb27d46c2e1558228d0fcf1
本章译文最后维护日期:2024-05-02
以下属性用于改变类型的使用方式。
The non_exhaustive
attribute
non_exhaustive
属性
non_exhaustive
属性表示类型或变体将来可能会添加更多字段或变体。它可以应用在结构体(struct
)上、枚举(enum
)上 和 枚举变体上。
non_exhaustive
属性使用 MetaWord元项属性句法,因此不接受任何输入。
在当前(non_exhaustive
限制的类型的)定义所在的 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, } // 非穷尽结构体可以在定义它的 crate 中正常构建。 let config = Config { window_width: 640, window_height: 480 }; let token = Token; let id = Id(4); // 非穷尽结构体可以在定义它的 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); // 非穷尽枚举可以在定义它的 crate 中进行详尽匹配 match error { Error::Message(ref s) => { }, Error::Other => { }, } match message { // 非穷尽变体可以在定义它的 crate 中进行详尽匹配 Message::Send { from, to, contents } => { }, Message::Reaction(id) => { }, Message::Quit => { }, } }
在定义所在的 crate之外,标注为 non_exhaustive
的类型须在添加新字段或变体时保持向后兼容性。
非穷尽类型(non-exhaustive types)不能在定义它的 crate 之外构建:
- 非穷尽变体(结构体(
struct
)或枚举变体(enum
variant))不能用 StructExpression句法(包括函数式更新(functional update)句法)构建。 - 类单元结构体隐式定义的同名常量或 元组结构体里隐含的和元组结构体同名的构造函数的可见性不大于
pub(crate)
。 也就是说,如果结构的可见性是pub
,则这种常量或构造函数的可见性是pub(crate)
,否则两类程序项的可见性是相同的(就像没有#[non_exhaustive]
的情况一样)。 - 枚举(
enum
)实例能被构建。
当超出其定义的 crate 时,以下构造示例不能编译:
示例:(译者注:本例把上例看成本例的 upstream
)
// 这些类型(`Config`、`Error` `Message`)是在上游 crate 中定义的类型,这些类型已被标注为 `#[non_exhaustive]`。
use upstream::{Config, Error, Message};
// 不能构造 `Config` 的实例,如果在 `upstream` 的新版本中添加了新字段,则本地编译会失败,因此不允许这样做。
let config = Config { window_width: 640, window_height: 480 };
// 无法构造 `Token` 的实例;如果添加了新字段,那么它将不再是类单元结构体,因此由它作为类单元结构体创建的同名常量在此crate 外则不再是公共可见的;这段代码无法编译。
let token = Token;
// 无法构造 `Id` 的实例;如果添加了新字段,则其构造函数签名将发生变化,因此其构造函数在此crate 外不再是公共可见的;这段代码无法编译。
let id = Id(5);
// 可以构造 `Error` 的实例;引入的新变体不会导致编译失败。
let error = Error::Message("foo".to_string());
// 无法构造 `Message::Send` 或 `Message::Reaction` 的实例,
// 如果在 `upstream` 的新版本中添加了新字段,则本地编译失败,因此不允许。
let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), };
let message = Message::Reaction(0);
// 无法构造 `Message::Quit` 的实例,
// 如果 `upstream` 内的 `Message::Quit` 的因为添加字段变成元组变体(tuple-variant/tuple variant)后,则本地编译失败。
let message = Message::Quit;
在定义所在的 crate 之外对非穷尽类型进行匹配,有如下限制:
- 当模式匹配一个非穷尽变体(结构体(
struct
)或枚举变体(enum
variant))时,必须使用 StructPattern句法进行匹配,其匹配臂必须有一个为..
。元组变体的构造函数的可见性降低的不会比pub(crate)
大。 - 当模式匹配在一个非穷尽的枚举(
enum
)上时,增加对单个变体的匹配无助于匹配臂需满足枚举变体的穷尽性(exhaustiveness)的这一要求。
在定义的 crate 之外时,以下匹配示例不能被编译:
示例:(译者注:可以把上上例看成本例的 upstream
)
// 这些类型(`Config`、`Error` `Message`)是在上游 crate 中定义的类型,这些类型已被标注为 `#[non_exhaustive]`。
use upstream::{Config, Error, Message};
// 不包含通配符匹配臂,无法匹配非穷尽枚举。
match error {
Error::Message(ref s) => { },
Error::Other => { },
// 加上 `_ => {},` 就能编译通过
}
// 不包含通配符匹配臂,无法匹配非穷尽结构体
if let Ok(Config { window_width, window_height }) = config {
// 加上 `..` 就能编译通过
}
// 无法匹配非穷尽类单元结构体或元组结构体,除非使用带通配符的语法。
// 使用 `let Token { .. } = token;` 这样的表达方式则可以通过编译
let Token = token;
// 使用 `let Id { 0: id_number, .. } = id;` 这样的表达方式则可以通过编译
let Id(id_number) = id;
match message {
// 没有通配符,无法匹配非穷尽(结构体/枚举内的)变体
Message::Send { from, to, contents } => { },
// 无法匹配非穷尽元组或单元枚举变体(unit enum variant)。
Message::Reaction(type) => { },
Message::Quit => { },
}
也不允许对外部 crate 的非穷尽类型做强转(case)操作。
use othercrate::NonExhaustiveEnum;
// 不能对非本地crate里的非穷尽枚举类型做cast
let _ = NonExhaustiveEnum::default() as u8;
非穷尽类型最好放在下游 crate 里。