为什么在调用带有按值自取值的方法并同时调用方法的参数时借用移动的值?

Mat*_*wik 5 rust borrow-checker

我遇到了一个问题,迫使我将一个好的内衬分割成{}带中间层的块let。我根本不清楚这个原因。在这个最小的示例中,我能够找出问题所在:

struct AB {
    a: u8,
    b: u8,
}

impl AB {
    fn foo(&self) -> String {
        String::from("foo")
    }
    fn bar(self, x: String) -> String {
        format!("{} - {} - {}!", x, self.a, self.b)
    }
}

fn main() {
    let x = AB { a: 3, b: 5 };
    let result = x.bar(x.foo());
    println!("{}", result);
}
Run Code Online (Sandbox Code Playgroud)

我的印象是该bar函数的参数将调用之前进行评估bar。在执行期间foo借入x,但在返回时String借用完成,因为String这不是参考轴承x的寿命。当bar被调用时,foo就借用应该结束了。

但是,编译器不同意:

struct AB {
    a: u8,
    b: u8,
}

impl AB {
    fn foo(&self) -> String {
        String::from("foo")
    }
    fn bar(self, x: String) -> String {
        format!("{} - {} - {}!", x, self.a, self.b)
    }
}

fn main() {
    let x = AB { a: 3, b: 5 };
    let result = x.bar(x.foo());
    println!("{}", result);
}
Run Code Online (Sandbox Code Playgroud)

我并不反对bar确实如此的事实x。我的问题是在转移发生foo借用的陈述。x

一个简单(但难看)的修复程序:

struct AB {
    a: u8,
    b: u8,
}

impl AB {
    fn foo(&self) -> String {
        String::from("foo")
    }
    fn bar(self, x: String) -> String {
        format!("{} - {} - {}!", x, self.a, self.b)
    }
}

fn main() {
    let x = AB { a: 3, b: 5 };
    let y = x.foo();
    let result = x.bar(y);
    println!("{}", result);
}
Run Code Online (Sandbox Code Playgroud)

x.foo()中间变量的赋值分隔开可以y很好地编译,这证实了我的期望,即借入确实foo返回了一次,但是为什么这样做有效呢?关于评估顺序,我是否不了解?为什么我不能摆脱中间体let y

Seb*_*edl 5

为了借用,评估顺序是从左到右。

这意味着bar调用的主题,即“移出x”提及,xfoo调用中的“借用”提及之前被考虑,因此,编译器认为该变量已从中移出。

对于外部提及是可变借用的类似情况,RFC 2025已被接受为解决方案,但尚未实现。不幸的是,该RFC似乎没有涵盖您的情况,因为外部使用是一个举动。

  • 我认为我的部分困惑来自于 OOP 风格的方法语法糖。`x(a,b,c)` 看起来像一个包含 3 个参数的函数。从左到右的评估在那里是有道理的,但由于 `x` 是“在括号之外”,我错误地将它算作“不是函数调用本身的一部分”。但是通过确实将其视为`AB::bar(x, x.foo())` 就清楚了。谢谢 (2认同)
  • 那么为什么这会起作用呢?`让 mut v = vec![0, 1, 2]; v.push(v.len());` (2认同)