问题
I'm trying to grok Rust's ownership model. I'm trying to pass a reference to a containing object when calling a function on a struct.
Here's my struct:
pub struct Player {}
impl Player {
pub fn receive(self, app: &App) {
}
}
As you can see, receive
expects a reference to an App
object.
pub struct App {
pub player: Player,
}
impl App {
pub fn sender(self) {
// how to call player.test() and pass self as a reference?
self.player.receive(&self);
}
}
The above code gives me "use of partially moved value: self
". Which makes sense, because App
has move semantics so the value was moved into the sender
function when it was called.
If I change it so that sender
takes a reference to self
instead, I get "cannot move out of borrowed content", which also sort of makes sense because we've borrowed the reference to self
when we went into the sender
function.
So what do I do? I understand why I can't store a reference to App
inside Player
, since that would lead to a doubly-linked structure. But I should be able to borrow a reference and perform operations on it, no?
I couldn't find an answer in the official tutorial.
I solved it by passing self
as a reference in receive
. But what if I want app
to be mutable in receive
? I can't pass self
as mutable in sender
because I'm also borrowing player
as mutable.
回答1:
because
App
has move semantics so the value was moved into thesender
function when it was called.
It's true that it was moved into sender
, but that's not what this message is about. Because Player::receive
takes self
by value, you actually had to decompose app
and move player
out of it to be able to call receive
. At that point in time, app
is now half-formed; it has no valid value for player
! If receive
tried to access app.player
, it would be using invalid memory.
"cannot move out of borrowed content" [...] because we've borrowed the reference to
self
when we went into thesender
function.
Right, which ties into above. Because we are borrowing an App
, we cannot move player
out of it, leaving the App
in a invalid state.
I should be able to borrow a reference and perform operations on it, no?
And you can, so long as the thing you are taking a reference to is completely formed at that point. There were also two hints in the above exposition:
If
receive
tried to accessapp.player
If you don't access
app.player
inreceive
, restructure your code to pass the other components ofApp
instead of the entire container. Maybe you have someGameState
that is really what you want to pass.leaving the
App
in a invalid stateYou can use something like mem::replace to put in a different
Player
intoapp
. Then it's still completely (but differently) formed and can have a reference to it taken again.
Of course, the more practical solution is to change to accept references (&self
).
But what if I want
app
to be mutable inreceive
?
Yup! You'd get "cannot borrow *self
as mutable more than once at a time". The solutions are actually basically the same, however! Decompose your App
into smaller, non-overlapping pieces or disassociate player
from self
before calling the method.
回答2:
One way to follow Shepmaster's solution
disassociate
player
fromself
before calling the method.
Is to put the player
in an Option:
impl App {
pub fn sender(&mut self) {
let mut player = self.player.take();
player.receive(&mut self);
self.player = Some(player);
}
}
One last resource is to use RefCell.
来源:https://stackoverflow.com/questions/36936221/pass-self-reference-to-contained-objects-function