Rust中match随处可见,但是其中有一些细节值得注意:被match的对象可以是值也可以是引用,pattern可以是值也可以是引用,这就有4种组合,各自是什么行为呢?
** 测试类 **
1 | use std::fmt; |
match对象和pattern都是值 (1)
1 |
|
运行结果:
1 | --------match_val_by_val-------- |
这种match,相当于每个成员被复制(move或copy),但整体object没有被复制(move或copy),也就是说,match obj
的时候:
obj
不会被复制(move或copy);obj
里的每个成员被复制(move或copy);
之前我一直认为是在pattern那里构造一个新对象,obj
被复制(move或copy)到那里。这是不对的。看上面例子中的b
:假如像我之前认为的那样,b
在match
的时候就被复制(就是move,因为Bar
不是Copy),之后打印它就会编译失败。所以,强调一下:match obj
的时候,obj
这个对象本身(作为一个整体)没有被复制(move或copy):
- 若
obj
里的每个成员都是Copy类型,那么match obj
之后,obj
是完好无损的(obj
本身没被move或copy,成员被copy了而已),例如b
; - 若
obj
里存在非Copy类型的成员,那么match obj
之后,obj
就被partial move
了(partial就是指那些不是Copy类型的成员)例如f
和b1
。可以使用ref
关键字来引用非Copy成员,而避免复制(move);
match对象和pattern都是ref (2)
1 |
|
运行结果:
1 | --------match_ref_by_ref-------- |
和match_val_by_val
一致,只是不会发生partial move
而是会编译失败(因为引用不能被move):
- 若
obj
里的每个成员都是Copy类型,那么match &obj
之后,obj
是完好无损的(obj
本身没被move或copy——显而易见,我们match的是引用,都没有ownership谈何move——成员被copy了而已),例如b
; - 若
obj
里存在非Copy类型的成员,那么match &obj
会编译失败(需要move,但我们match的是引用,没有ownership),例如注释掉的个match
语句。可以使用ref
关键字来引用非Copy成员,而避免复制(move);
match对象是ref但pattern是值 (3)
1 |
|
运行结果:
1 | --------match_ref_by_val-------- |
这种最简单:
match &obj
不可能使obj被move(显而易见,我们match的是引用,没有ownership);- pattern中的每个字段都是
obj
中对应字段的引用;
match对象是值但pattern是ref (4)
1 |
|
运行结果:
1 | error[E0308]: mismatched types |
小结 (5)
对于(1)match对象和pattern都是值和(2)match对象和pattern都是ref这两种情况来说:Rust试图对obj的成员一一复制(move或copy),仅此而已,不复制obj这个变量本身。对于(1)可能导致对象被partial move
;对于(2)可能导致编译错误。两者都是由obj中存在非Copy成员,对它们的复制(即move)导致的。且两者都可以使用ref
关键字来引用非Copy成员,而避免复制(move)它们。
所以,(3)match对象是ref但pattern是值有点像是一个优化:所有成员,无论是不是Copy,都不复制(move或copy),一律引用。
个人理解是,*(2)*应该少用,根据程序的上下文:若match以后永远不再需要访问obj就使用(1);否则,还需要保留obj就使用(3)。