匹配人体工程学保留
摘要
- 只有当模式直到绑定处都是完全显式的(即,当它不使用匹配人体工程学时),才允许在绑定上编写
mut
、ref
或ref mut
。- 换句话说,当默认绑定模式不是
move
时,在绑定上编写mut
、ref
或ref mut
是错误的。
- 换句话说,当默认绑定模式不是
- 引用模式 (
&
或&mut
) 仅允许在模式的完全显式前缀中使用。- 换句话说,只有当默认绑定模式为
move
时,引用模式才能匹配被检查值的引用。
- 换句话说,只有当默认绑定模式为
详情
背景
在 match
、let
和其他结构中,我们将模式与被检查值进行匹配。例如:
#![allow(unused)] fn main() { let &[&mut [ref x]] = &[&mut [()]]; // x: &() // ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ // Pattern Scrutinee }
这样的模式被称为完全显式模式,因为它不会省略(即“跳过”或“忽略”)被检查值中的任何引用。相比之下,这个其他方面等效的模式不是完全显式的
#![allow(unused)] fn main() { let [[x]] = &[&mut [()]]; // x: &() }
像这样的模式被称为使用匹配人体工程学,最初在 RFC 2005 中引入。
在匹配人体工程学下,当我们逐步将模式与被检查值进行匹配时,我们会跟踪默认绑定模式。此模式可以是 move
、ref mut
或 ref
之一,并且它从 move
开始。当我们到达绑定时,除非提供了显式绑定模式,否则将使用默认绑定模式来决定绑定的类型。
例如,在这里我们提供了一个显式绑定模式,导致 x
通过引用绑定
#![allow(unused)] fn main() { let ref x = (); // &() }
相比之下
#![allow(unused)] fn main() { let [x] = &[()]; // &() }
在这里,在模式中,我们传递了被检查值中的外部共享引用。这导致默认绑定模式从 move
切换到 ref
。由于没有指定显式绑定模式,因此在绑定 x
时使用 ref
绑定模式。
mut
限制
在 Rust 2021 及更早版本中,我们允许这种奇怪的情况
#![allow(unused)] fn main() { let [x, mut y] = &[(), ()]; // x: &(), mut y: () }
在这里,因为我们在模式中传递了共享引用,所以默认绑定模式切换到 ref
。但是,在这些版本中,在绑定上写入 mut
会将默认绑定模式重置为 move
。
这可能会令人惊讶,因为可变性应该影响类型并不直观。
为了留出空间来修复这个问题,在 Rust 2024 中,当默认绑定模式不是 move
时,在绑定上编写 mut
是错误的。也就是说,只有当模式(直到该绑定处)是完全显式的时,才能在绑定上编写 mut
。
在 Rust 2024 中,我们可以将上面的例子写成
#![allow(unused)] fn main() { let &[ref x, mut y] = &[(), ()]; // x: &(), mut y: () }
ref
/ ref mut
限制
在 Rust 2021 及更早版本中,我们允许
#![allow(unused)] fn main() { let [ref x] = &[()]; // x: &() }
在这里,ref
显式绑定模式是冗余的,因为通过传递共享引用(即在模式中不提及它),绑定模式切换到 ref
。
为了为其他语言可能性留出空间,我们不允许在 Rust 2024 中使用冗余的显式绑定模式。我们可以将上面的例子简单地重写为
#![allow(unused)] fn main() { let [x] = &[()]; // x: &() }
引用模式限制
在 Rust 2021 及更早版本中,我们允许这种奇怪的情况
#![allow(unused)] fn main() { let [&x, y] = &[&(), &()]; // x: (), y: &&() }
在这里,模式中的 &
既匹配 &()
上的引用,又将默认绑定模式重置为 move
。这可能会令人惊讶,因为模式中的单个 &
会导致类型发生比预期更大的变化,从而删除了两层引用。
为了留出空间来修复这个问题,在 Rust 2024 中,当默认绑定模式不是 move
时,在模式中编写 &
或 &mut
是错误的。也就是说,只有当模式(直到该点)是完全显式的时,才能编写 &
或 &mut
。
在 Rust 2024 中,我们可以将上面的例子写成
#![allow(unused)] fn main() { let &[&x, ref y] = &[&(), &()]; }
迁移
rust_2024_incompatible_pat
lint 标记了 Rust 2024 中不允许的模式。此 lint 是 rust-2024-compatibility
lint 组的一部分,该组在运行 cargo fix --edition
时会自动应用。此 lint 将自动将受影响的模式转换为完全显式模式,这些模式在 Rust 2024 和所有早期版本中均可正确工作。
要迁移您的代码以与 Rust 2024 兼容,请运行
cargo fix --edition
例如,这将转换这个...
#![allow(unused)] fn main() { let [x, mut y] = &[(), ()]; let [ref x] = &[()]; let [&x, y] = &[&(), &()]; }
...变成这个
#![allow(unused)] fn main() { let &[ref x, mut y] = &[(), ()]; let &[ref x] = &[()]; let &[&x, ref y] = &[&(), &()]; }
或者,您可以手动启用 lint 以查找需要迁移的模式
#![allow(unused)] fn main() { // Add this to the root of your crate to do a manual migration. #![warn(rust_2024_incompatible_pat)] }