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.
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 differentPlayer
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.
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