Rust 最强大的功能之一是通过 match 语句实现的模式匹配。随着 Rust 1.26 的发布,出现了 "match ergonomics" 这一术语——一个新的解包和自动解引用引用值的逻辑,旨在减少模式中显式的 ref 和 * 的数量。
在引入 match ergonomics 之前,在使用将值包装在引用中的枚举(例如 Option<&T>)时,必须显式地指明模式的解引用:
let opt: Option<&i32> = Some(&10); match opt { Some(&val) => { /* ... */ }, None => {}, }
这降低了可读性,并增加了在复杂结构中出错的可能性。
引入 match ergonomics 后,Rust 会自动解引用模式中的引用,使得可以编写更简单和自然的代码:
let opt: Option<&i32> = Some(&10); match opt { Some(val) => println!("{}", val), None => (), }
编译器 "理解" 需要将 &i32 解引用为 i32,以便于程序员使用。这一特性显著简化了与 Option、Result 类型及其与引用的组合的交互。
关键特性:
* 和 ref使用 ref 和对值的引用(例如,ref val 与 &val)有什么区别?
ref 创建指向位于栈上的值的引用,而 &val 与已经存在的引用进行匹配。这一点很重要,例如,如果您需要额外的引用来处理可变性。
示例:
let x = 5; match x { ref r => println!("ref: {}", r), // r: &i32 }
在 Option<&T> 中使用可变引用时,模式匹配会发生什么?
自动解引用对于可变引用也有效。如果您有 Option<&mut T>,那么可以通过匹配直接获得可变变量。
示例:
let mut x = 5; let opt = Some(&mut x); match opt { Some(val) => *val += 1, None => {} }
match ergonomics 会导致不明显的借用或所有权错误吗?
可能会,如果没有意识到在匹配内部创建了一个临时借用,这个借用对整个匹配分支有效,并可能阻塞对该值的其他操作。
* 和 ref,忽略 match ergonomics,降低代码的简洁性和可读性新手继续使用旧风格 Some(&val) 或 Some(ref val),在更新 Rust 到新版本时遇到错误或不可预测的行为。
优点: 代码在早期版本的 Rust 上运行,并且对那些在 match ergonomics 出现之前学习的人而言是清晰的。
缺点: 表达性低,更新编译器后出错的风险。
程序员使用现代的 match ergonomics,代码简洁高效,利用自动解引用。
优点: 简洁易读,出错的风险较低。
缺点: 对于旧版本的 Rust,此类代码不适用,可能会引起那些不太熟悉现代习惯的人的误解。