Rust 如何匹配包含&;的元组;mut enum并在匹配臂中使用该enum?

Rust 如何匹配包含&;的元组;mut enum并在匹配臂中使用该enum?,rust,pattern-matching,borrow-checker,Rust,Pattern Matching,Borrow Checker,如何编译下面的代码?它看起来非常安全,但无法让编译器相信它是安全的 与*self匹配的版本出现错误:无法移出匹配行上的借用内容 与self匹配的版本给出:移动值的使用:*self enum Foo { Foo1(u32), Foo2(i16), } impl Foo { fn bar(&mut self, y: u32) -> (u32, &mut Foo) { match (*self, y) { (Foo

如何编译下面的代码?它看起来非常安全,但无法让编译器相信它是安全的

*self
匹配的版本出现错误:
无法移出匹配行上的借用内容

self
匹配的版本给出:
移动值的使用:*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)
            }
        }
    }
}
我觉得我需要一个匹配臂,如下所示,但它似乎不是有效的语法:

(ref mut f @ Foo::Foo1, b) if (b == 5) => {
    print!("is five");
    f.0 = b + 42;
    (b, f)
} 
错误[E0532]:应为单元结构/变量或常量,找到元组变量`Foo::Foo1`
-->src/main.rs:24:30
|
如果(b==5)=>{
|^^^^^^^^^^^^^^不是单位结构/变量或常量

不,这不安全。您正试图在匹配臂内引入可变别名。可变引用
a
指向与
self
相同的值。可以更改
self
(例如
*self=Foo::Foo1(99)
)这将使
a
无效,因此不允许使用此代码

相反,在
match
语句中可变地重新箭头
self
,并让它返回元组的第一个值。由于此值没有对
self
的引用,因此您可以返回
self
以及
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)
    }
}
但是,像这样返回
self
在这里是毫无意义的。调用者已经有一个
&mut Foo
,因此您不需要“返回”。这允许简化为:

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,
        }
    }
}
我仍然认为这是一个安全的操作,尽管编译器可能无法理解这一点

使用,借阅检查器变得更加智能。添加了显式重新借阅的原始代码将编译:

#![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),
        }
    }
}
另见:


  • 不,这不安全。您正试图在匹配臂内引入可变别名。可变引用
    a
    指向与
    self
    相同的值。可以更改
    self
    (例如
    *self=Foo::Foo1(99)
    )这将使
    a
    无效,因此不允许使用此代码

    相反,在
    match
    语句中可变地重新箭头
    self
    ,并让它返回元组的第一个值。由于此值没有对
    self
    的引用,因此您可以返回
    self
    以及
    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)
        }
    }
    
    但是,像这样返回
    self
    在这里是毫无意义的。调用者已经有一个
    &mut Foo
    ,因此您不需要“返回”。这允许简化为:

    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,
            }
        }
    }
    
    我仍然认为这是一个安全的操作,尽管编译器可能无法理解这一点

    使用,借阅检查器变得更加智能。添加了显式重新借阅的原始代码将编译:

    #![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),
            }
        }
    }
    
    另见:


    “似乎非常安全”,我的C代码似乎也非常安全,但rust不是关于“似乎”,而是“我现在正在阅读链接的问题,但您的解决方案仍然让我使用移动值:*self注意,我正在尝试返回self,错误发生时“似乎非常安全”,我的C代码看起来也很安全,但生锈不是“看起来”而是“是”我现在正在阅读链接的问题,但您的解决方案仍然让我使用移动值:*self注意,我正在尝试返回self,其中错误发生感谢输入-虽然别名仅在返回期间发生,因此我仍然认为这是一个安全的操作,尽管编译器可能无法理解然而,你的回答也指出我的例子被简化了太多——在真实场景中,数据结构是自引用的,我实际上返回了原始的子树,我会调整我的问题,以更好地反映这个@kosVandra编辑问题,从而使现有的答案无效,这通常是不赞成的pon.抱歉,恢复了更改,并在此处提交了一个新问题:@kosVandra和我对NLL的编辑没有恰好解决您的问题?感谢您的输入-虽然别名只在返回期间发生,所以我仍然认为这是一个安全操作,尽管编译器可能无法理解。但是您的回答也指出了这一点我的例子被简化得太多了——在真实场景中,数据结构是自引用的,我实际上返回了原始数据的子树,我将调整我的问题以更好地反映这个@kosVandra编辑问题,这样它们会使现有答案无效,这通常是不受欢迎的。抱歉,恢复了更改,然后提交这里提出了一个新问题:@ÁkosVandra和我对NLL的编辑恰好没有解决您的问题?