【问题标题】:How can I bind a variable in a match arm when matching on a mutable reference?在可变引用上匹配时,如何在匹配臂中绑定变量?
【发布时间】:2015-06-23 16:30:48
【问题描述】:

我在一个结构上matching,想使用一个匹配守卫。但是,该结构是可变的,并且匹配臂左侧的绑定变量似乎会导致单独的借用。然后这会触发编译错误,因为当可变借用未完成时,您不能有第二次借用(可变或不可变)。

struct A(u8);

impl A {
    fn is_awesome(&self) -> bool { true }
}

struct Container(A);

impl Container {
    fn update(&mut self) {}

    fn do_a_thing(&mut self) {
        match *self {
            Container(ref a) if a.is_awesome() => self.update(),
            _ => {},
        }
    }
}

fn main() {}
error[E0502]: cannot borrow `*self` as mutable because `self.0` is also borrowed as immutable
  --> src/main.rs:14:51
   |
14 |             Container(ref a) if a.is_awesome() => self.update(),
   |                       -----                       ^^^^ mutable borrow occurs here
   |                       |
   |                       immutable borrow occurs here
15 |             _ => {},
16 |         }
   |         - immutable borrow ends here

我目前的解决方法是在我的比赛之前复制计算比赛守卫的逻辑,然后我可以只使用布尔值作为我的比赛守卫。对于明显的代码重复问题,这并不令人满意:

fn do_a_thing(&mut self) {
    let awesome = match *self {
        Container(ref a) => a.is_awesome(),
    };

    match *self {
        Container(..) if awesome => self.update(),
        _ => {},
    }
}

【问题讨论】:

  • 我自己会把match self { &mut Container(ref a) => … }改写成let a = &self.0; …
  • @ChrisMorgan 是的,我也是。使用模式匹配而不添加枚举或其他嵌套层以试图保持示例较小是一种自负。
  • (在这种情况下,流行的样式也是match *self { Container(ref a) => … },以取出匹配主题中的任何引用而不是分支模式。)
  • @ChrisMorgan 很好,这是我在围栏上试验的剩余物!

标签: pattern-matching rust


【解决方案1】:

non-lexical lifetimes are enabled 时,您的原始代码按原样运行。

在非词法生命周期之前

以安全的名义,Rust 禁止各种类别的事物,即使它们的特定情况可能有效。这是一种这样的情况,您尝试做的事情是不可能的,也永远不可能。

您已经创建了对self 内容的引用,但随后您调用了self.update(),它需要对self 的可变引用。一种语言可以有效地内联update,从而确定保持该引用是安全的,但很容易证明基本概念并不总是适用于Rust编译器的这个坏例子拯救你:

struct A(u8);

struct Container(A);

impl Container {
    fn update(&mut self) {
        self.0 = A(0);
    }

    fn do_a_thing(&mut self) {
        let a = &self.0;
        let before = a.0;
        self.update();
        assert_eq!(before, a.0);
    }
}

fn main() {
    Container(A(1)).do_a_thing();
    // Panic: 1 != 0
}

如果允许编译,它会恐慌,因为 a 的目标,尽管它是一个不可变的引用,但在你下面发生了变化,这显然不能允许这样做。

C++ 模板的随遇而安的心态是尝试某些可能有效也可能无效的示例;对他们来说,一个函数内部的深层变化很可能会破坏它的用户,从而使他们不再编译。 Rust 决定不走这条路,因此将每种方法都视为强大的隔离屏障。对函数体的任何更改都不会导致方法外的代码停止编译。

当您在self 上调用&mut self-requesting 方法时,您不能对self 内的任何内容有任何引用,无论是可变的还是其他的。

【讨论】:

  • 我与 Rust 编译器在我应该做什么和不应该做什么方面存在相当大的分歧。与 rustc 争论有点烦人,因为我从来没有赢得过争论。如果我想得够久,我可以总是找到我想做的事情错了的原因。
  • 我对编译器一点也不生气,我完全同意这个推理。在我的理想情况下,我希望有一种方法可以在匹配臂的左侧绑定一个变量,并且在匹配体开始之前以某种方式让绑定“超出范围”。那将允许我在比赛守卫中调用我的方法,但仍然会从绑定变量向上变异。
  • 我认为您没有比我目前的解决方法更清洁的解决方案?
  • 不,不要害怕,当我们还在使用词法借用时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多