如何模式匹配包含 &mut 枚举的元组并在匹配臂中使用该枚举? [英] How can I pattern match a tuple containing a &mut enum and use the enum in the match arm?

查看:48
本文介绍了如何模式匹配包含 &mut 枚举的元组并在匹配臂中使用该枚举?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码如何编译?看起来很安全,但不能说服编译器它是.

匹配*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:

这篇关于如何模式匹配包含 &amp;mut 枚举的元组并在匹配臂中使用该枚举?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆