我遇到了一个问题,被迫将一个漂亮的一行代码拆分成带有中间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);
}
我曾认为 bar
函数的参数会在调用 bar
之前被评估。在其执行期间,foo
借用了 x
,但当它返回其 String
时,借用已经结束了,因为 String
不是承载 x
生命周期的引用。当调用 bar
时,foo
的借用应该已经结束。
然而,编译器并不这样认为:
error[E0382]: borrow of moved value: `x`
--> src/main.rs:17:24
|
17 | let result = x.bar(x.foo());
| - ^ value borrowed here after move
| |
| value moved here
|
= note: move occurs because `x` has type `AB`, which does not implement the `Copy` trait
我不反对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);
}
将x.foo()
的赋值分配给一个中间变量y
可以编译通过,证实了我的预期,即一旦foo
返回,借用确实结束了,但是为什么会这样呢?我是否对评估顺序有所不理解?为什么我不能摆脱中间的let y
?
x(a,b,c)
看起来像一个涉及三个参数的函数。从左到右的计算顺序在这里是有意义的,但由于x
在“括号外面”,我错误地将其视为“不属于函数调用本身的一部分”。但通过将其确实视为AB::bar(x, x.foo())
可以使其更加清晰明了。 - Mathijs Kwiklet mut v = vec![0, 1, 2]; v.push(v.len());
- Boiethios&mut
情况,但移动不是借用,也没有生命周期,因此在语义上真正不同。然而,我同意看起来self
-taking情况也可以以类似的方式处理。 - trent&mut
情况。 您能详细说明一下原始&mut v
借用被拆分为哪两个子借用吗?PS:我注意到尽管v.push(v.len())
可以编译,但Vec::push(&mut v, v.len())
不能。幕后必须有一些“Deref”魔术。 - nalzokv.push(v.len())
发生的情况),&mut
借用最初被视为共享借用,然后被“升级”为完全可变借用,以便调用函数。我无法比rustc dev guide更好地解释这个概念,它也证实了你对v.push
有点奇怪的猜测;但是,这次不是Deref
,而是自动引用。 - trent