问题
I ran into an issue that forces me to split a nice oneliner into a {}
block with an intermediate let
. The reason for this isn't clear to me at all. I was able to isolate the issue in this minimal example:
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);
}
I was under the impression that the arguments to the bar
function would be evaluated before calling bar
. foo
borrows x
during its execution, but when it returns its String
, the borrow is finished, as String
is not a reference bearing x
's lifetime. When bar
gets called, foo
's borrow should be over.
However, the compiler disagrees:
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
I'm not disagreeing with the fact that bar
indeed moves x
. My issue is with the statement that foo
borrows x
after the move takes place.
A simple (but ugly) fix:
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);
}
separating the assignment of x.foo()
to an intermediate variable y
compiles fine, confirming my expectation that the borrow is indeed over once foo
returns, but why does this work? Is there something I don't understand about evaluation order? Why can't I get rid of the intermediate let y
?
回答1:
Evaluation order, for the purpose of borrows, is from left to right.
This means that the subject of the bar
call, the "move-out" mention of x
, is considered before the "borrow" mention of x
in the foo
call, and therefore, the compiler considers the variable to have been moved from.
For the similar case where the outer mention is a mutable borrow, RFC 2025 has been accepted as a solution, but hasn't been implemented yet. Unfortunately this RFC doesn't appear to cover your case, where the outer use is a move.
来源:https://stackoverflow.com/questions/55117392/why-is-there-a-borrow-of-a-moved-value-when-calling-a-method-that-takes-self-by