如何模式匹配包含 &mut 枚举的元组并在匹配臂中使用该枚举? [英] How can I pattern match a tuple containing a &mut enum and use the enum in the match arm?
问题描述
下面的代码如何编译?看起来很安全,但不能说服编译器它是.
匹配*self
的版本报错:cannot move out of Bored content
在匹配的那一行
匹配self
的版本给出:使用移动值:*self
enum Foo {Foo1(u32),Foo2(i16),}impl Foo {fn bar(&mut self, y: u32) ->(u32, &mut Foo) {匹配 (*self, y) {(Foo::Foo1(ref mut a), b) if (b == 5) =>{打印!(是五");*a = b + 42;(二、自己)}(Foo::Foo2(ref mut a), b) if (b == 5) =>{打印!(是五");*a = (b + 42) 作为 i16;(*a * b, 自己)}_ =>{print!("不是五个!");(y, 自己)}}}}
我觉得我需要一个像下面这样的匹配臂,但它似乎不是有效的语法:
(ref mut f @ Foo::Foo1, b) if (b == 5) =>{打印!(是五");f.0 = b + 42;(b, f)}
error[E0532]: 预期的单元结构/变量或常量,发现元组变量 `Foo::Foo1`-->src/main.rs:24:30|24 |(ref mut f @ Foo::Foo1, b) if (b == 5) =>{|^^^^^^^^^ 不是单位结构/变体或常数
不,这不安全.您正在尝试在匹配臂内引入可变别名.可变引用 a
指向与 self
相同的值.有可能改变 self
(例如 *self = Foo::Foo1(99)
),这会使 a
无效,所以这段代码是不允许的.
相反,在 match
语句中可变地重新借用 self
并让它返回元组的第一个值.由于此值没有对 self
的引用,因此您可以使用 match
的结果返回 self
:
enum Foo {Foo1(u32),Foo2(u32),//改变了所以我不必弄清楚你的意思是什么}impl Foo {fn bar(&mut self, y: u32) ->(u32, &mut Foo) {让下一个 = 匹配 (&mut *self, y) {(Foo::Foo1(a), b @ 5) =>{*a = b + 42;乙}(Foo::Foo2(a), b @ 5) =>{*a = b + 42;*a * b}_ =>是,};(下一个,自己)}}
然而,像这样返回 self
在这里是没有意义的.调用者已经有一个&mut Foo
,所以你不需要还给它".这允许简化为:
impl Foo {fn bar(&mut self, y: u32) ->u32 {匹配(自我,y){(Foo::Foo1(a), b @ 5) =>{*a = b + 42;乙}(Foo::Foo2(a), b @ 5) =>{*a = b + 42;*a * b}_ =>是,}}}
<块引用>
我仍然会说这是一个安全的操作,尽管编译器可能无法理解
使用非词法生命周期,借用检查器变得更加智能.您的原始代码添加了显式重新借用编译:
#![feature(nll)]枚举 Foo {Foo1(u32),Foo2(u32),//改变了所以我不必弄清楚你的意思是什么}impl Foo {fn bar(&mut self, y: u32) ->(u32, &mut Foo) {匹配 (&mut *self, y) {(Foo::Foo1(a), b @ 5) =>{*a = b + 42;(二、自己)}(Foo::Foo2(a), b @ 5) =>{*a = b + 42;(*a * b, 自己)}_ =>(y,自我),}}}
另见:
How can the code below be made to compile? It seems perfectly safe, but can't convince the compiler that it is.
The version matching *self
gives the error: cannot move out of borrowed content
on the line of the match
The version matching self
gives: use of moved value: *self
enum Foo {
Foo1(u32),
Foo2(i16),
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
match (*self, y) {
(Foo::Foo1(ref mut a), b) if (b == 5) => {
print!("is five");
*a = b + 42;
(b, self)
}
(Foo::Foo2(ref mut a), b) if (b == 5) => {
print!("is five");
*a = (b + 42) as i16;
(*a * b, self)
}
_ => {
print!("is not five!");
(y, self)
}
}
}
}
I feel like I would need a match arm such as the following, but it doesn't seem to be valid syntax:
(ref mut f @ Foo::Foo1, b) if (b == 5) => {
print!("is five");
f.0 = b + 42;
(b, f)
}
error[E0532]: expected unit struct/variant or constant, found tuple variant `Foo::Foo1`
--> src/main.rs:24:30
|
24 | (ref mut f @ Foo::Foo1, b) if (b == 5) => {
| ^^^^^^^^^ not a unit struct/variant or constant
No, this is not safe. You are attempting to introduce mutable aliasing inside the match arm. The mutable reference a
points into the same value as self
. It would be possible to change self
(e.g. *self = Foo::Foo1(99)
) which would then invalidate a
, so this code is disallowed.
Instead, mutably reborrow self
in the match
statement and have it return the first value of the tuple. Since this value doesn't have a reference to self
, you can then return self
with the result of the match
:
enum Foo {
Foo1(u32),
Foo2(u32), // changed so I don't have to figure out what casting you meant
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
let next = match (&mut *self, y) {
(Foo::Foo1(a), b @ 5) => {
*a = b + 42;
b
}
(Foo::Foo2(a), b @ 5) => {
*a = b + 42;
*a * b
}
_ => y,
};
(next, self)
}
}
However, returning self
like this is rather pointless here. The caller already has a &mut Foo
, so you don't need to "give it back". This allows simplifying to:
impl Foo {
fn bar(&mut self, y: u32) -> u32 {
match (self, y) {
(Foo::Foo1(a), b @ 5) => {
*a = b + 42;
b
}
(Foo::Foo2(a), b @ 5) => {
*a = b + 42;
*a * b
}
_ => y,
}
}
}
I would still say it is a safe operation, although the compiler may not be able to understand that
With non-lexical lifetimes, the borrow checker becomes more intelligent. Your original code with an added explicit reborrow compiles:
#![feature(nll)]
enum Foo {
Foo1(u32),
Foo2(u32), // changed so I don't have to figure out what casting you meant
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
match (&mut *self, y) {
(Foo::Foo1(a), b @ 5) => {
*a = b + 42;
(b, self)
}
(Foo::Foo2(a), b @ 5) => {
*a = b + 42;
(*a * b, self)
}
_ => (y, self),
}
}
}
See also:
- Why does matching on a tuple of dereferenced references not work while dereferencing non-tuples does?
- What is the syntax to match on a reference to an enum?
- How can I use match on a pair of borrowed values without copying them?
- Is there any difference between matching on a reference to a pattern or a dereferenced value?
这篇关于如何模式匹配包含 &mut 枚举的元组并在匹配臂中使用该枚举?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!