Rust编程入门实战 如何将可以使用 if作为“匹配看守”(matchguards)
下划线在 Rust里面用处很多,比如 : 在 match表达式中表示“其他分支”,在模式中作 为占位符,还可以在类型中做占位符,在整数和小数字面量中做连接符,等等 。
除了下划线可以在模式中作为“占位符”,还有两个点…也可以在模式中作为“占位 符”使用 。 下划线 表示省略一个元素,两个点 可以 表示省略多个元素 。 比如:
fn main() {
let x = (1, 2, 3);
let (a, ) = x; // 模式解构 println ! (”{}”, a);
如果我们希望只匹配阳pie 中的第一个元素,其他的省略,那么用一个下划线是不行的, 因为这样写,左边的阳pie和右边的 tuple不匹配。 修改方案有两种。 一种是:
let (a, , ) = x; II 用下划线 , 那么个数要匹配
另一种是:
let (a, ..) = x; // 用两个点 , 表示其他的全部省略
let (a, .., b) = x;II 用两个点 , 表示只省略所有元素也是可以的
7.2.4 Guards
可以使用 if作为“匹配看守”(matchguards)。 当匹配成功且符合 if条件,才执行后面的 语句。 示例如下:
enurn Optionalint {
Value(i32),
Missing,
let x = Optionalint: :Value (5);
match x {
Optional工nt: :Value(i) if i > 5 => println! (”Got an int bigger than five!"), Optional!nt: :Value( .. ) => println! ("Got an int!”),
Optional!nt: :Missing => println! (”No such luck.”),
}
在对变量的“值”进行匹配的时候,编译器依然会保证“完整无遗漏”检查 。 但是这个 检查目前做得并不是很完美,某些情况下会发生误报的情况,因为毕竟编译器内部并没有一 个完整的数学解算功能:
fn main() {
let x = 10;
match x {
i if i > 5 => println! (”bigger than five ”),
i if i <= 5 => println! (”small or equal to five"),}
}
从 if条件中可以看到,实际上我们已经覆盖了所有情况,可惜还是出现了编译错误。 编 译器目前还无法完美地处理这样的情况 。 我们只能再加入一条分支,单纯为了避免编译错误:
_ = >unreachab le !(),
编译器会保证 match 的所有分支合起来一定覆盖了目标的所有可能取值范围,但是并不 会保证各个分支是否会有重叠的情况(毕竟编译器不想做成一个完整的数学解算器) 。 如果不 同分支覆盖范围出现了重叠,各个分支之间的先后顺序就有影响了:
fn intersect(arg: i32) {
arg {
=> println ! (”default case"),
fn main() {
let x = -1;
intersect(x);
如果我们进行匹配的值同时符合好几条分支,那么总会执行第一条匹配成功的分支,忽 略其他分支 。
ref和 mut
如果我们需要绑定的是被匹配对象的引用, 则可以使用 ref关键字 :
let x = 5 i32; match x {
ref r => println!(”Got a reference to {}”, r), 11 此时 r 的类型是 、&i32、
之所以在某些时候需要使用 ref,是因为模式匹配的时候有可能发生变量的所有权转移, 使用 ref就是为了避免出现所有权转移 。
那么 ref关键字和引用符号&有什么关系呢? 考虑以下代码中变量绑定 x分别是什么 类型?
注意: ref是“模式”的 一 部分,它只能出现在赋值号左边,而&符号是借用运算符, 是表达式的一部分,它只能出现在赋值号右边 。
为了搞清楚这些 变量 绑定的分别是什么类型,我 们可以把 变量 的类型信息打 印出 来看 看。 有两种方案:
- 利用编译器的错误信息来帮我们理解 ;
- 利用标准库里面的 intrinsic 函数打印 。
这次提示的是,“useofpartiallymovedvalue、i”。 因为编译器认为这个match语句把内部的 String 变量移动出来了,所以后续的打印 x 的值是错误的行为 。 为了保证这个 match语句 不发生移动,我们需要把这个模式继续修改为 Some(ref rout i) ,这一次,编译通过了 。
这个问题还有更简单的修复方式 , 就是把 match x 改为 match &rout x :
fn main() {
let mut x : Option<String> = Some{”hello".into()); match &mut x {
Some(i) => i.push_str("world”), None=> println !( ”None” },
println!(”{:?}”, x);
在这种情况下,编译器没有报错,是因为我 们 对指针做了模式匹配,编译器很聪 明地 推理出来了,变量 i必须是一个指针类型,所以它帮我们 自动加了 ref rout模式,它通过 自动类型推导自己得出了结论,认为 i 的类型是 &rout String。 这是编译器专门做的一个 辅助功能。 在很多时候,特别是类型嵌套层次很多的时候,处处都要关心哪个 pattern是不 是要加个 rout或者 ref,其实是个很烦人的事情。
有了这个功能, 用户就不用每次都写麻 烦的 rout 或者 ref, 在一些显而易见的情况下,编译器自动来帮我们合理地使用 rout或者 ref。 读者可以用我们前面实现的 print_type_name试试,用 Some(i) 模式, 以及用 Some(ref rout i) 模式,变量 土的类型分别是什么。
结论是类型一样。 因为在我们不明确写出来 ref rout模式的时候,编译器帮我们做了更合理的自动类型推导 。
if-let和 while-let
Rust不仅能在 match表达式中执行‘模式解构”,在 let语句中, 也可以应用同样的模式。 Rust还提供了 if-let语法糖。 它的语法为 if l et PATTERN = EXPRESSION { BODY }。 后面可以跟一个可选的 else分支。
比如,我们有一个类型为 Option 的变量 optVal,如果我们需要取出里面的值, 可以采用这种方式 :
match optV al { Some(x) => {
doSomethingWith ( x ); => {}
这样做语法比较冗长,从变量 optVal 到执行操作的函数有两层缩进,我们还必须写 一 个不做任何操作的语句块才能满足语法要求。 换一种方式,通过 Option类型的方法,我们 可以这么做:
if optVal.is_some() { // 首先判断它一定是 Some(_)
let x = optVal.unwrap(); // 然后取出内部的数据
doSomethingWith(x);
}
从视觉上来看,代码缩进层次减少到了一层 。 但是它在运行期实际上判断了两次 。ptVal里面是否有值: 第一次是is some()函数,第二次是unwrap()函数。从执行效 率上来说是降低了的。 而使用 if-let语法, 则可以这么做:
if let Some(x) = optVal { doSometh 工 ngWith(x);
这其实是一个简单的语法糖,其背后执行的代码与 match 表达式相比,并无效率上的 差 别 。 它跟 match 的区别是: match 一定要完整匹配, if-let 只匹配感兴趣的某个特定的分支, 这种情况下的写法比 match简单点。 同理, while-let与 if-let一样,提供了在 while语句中使 用“模式解构”的能力,此处就不再举例 。
if-let 和 while”let 还支持模式的“或”操作(此功能目前尚未在编译器中实现) 。 比如, 我们有如下 enum定义:
enum E<T> {
A(T) I B(T) I C, D, E, F
函数和闭包参数做模式解构
示例如下 。 一个函数接受一个结构体参数,可以直接在参数这里做模式解构:
struct T { iteml: char,
item2: bool,
fn test( T{iteml: argl, item2: arg2} : T) { printlnl (”{}{}”, argl, arg2);
fn main() {
letx=T{ iteml :’且·,
item2: false,
};
test(x);
}
更多推荐
所有评论(0)