Well, first of all,
fn work(&mut self) { ... }
is an abbreviation for
fn work(self: &mut Self) { ... }
which in turn stands for
fn work(self: &mut MyStruct) { ... }
in the context of an impl MyStruct
.
So self
is a mutable reference to MyStruct
, i.e. a &mut MyStruct
.
This means, that in order to pass a MyStruct
and not a &mut MyStruct
to the modify
function, since modify
expects a MyStruct
, you cannot pass self
instead, but e.g. need to dereference the reference self
. Something like
*self = modify(*self);
This doesn’t quite compile yet, though.
The problem now is that you cannot easily take the value out of a &mut MyStruct
reference. At least for a type that isn’t Copy
the Copy
trait allows you to, among other things, do something like for example dereferencing a &mut i32
by copying the number behind the reference.
In order to do *self = modify(*self)
, using Rust’s move semantics, it would need to be possible to:
- take/move the value out of the
self: &mut MyStruct
reference
- then call
modify
- finally, move the result back into the place that
self
points to
however, if modify
were to exit with a panic, then work
would leave the reference self
pointing to no valid value, which doesn’t work.
The usual workaround for something like this is to use mem::replace or something similar, and put in an intermediate dummy value. E.g.
fn work(&mut self) {
let this = std::mem::replace(self, MyStruct { counter: 0 });
let this = modify(this);
*self = modify(this);
}
Another alternative is to copy the entire struct, e.g. like this
fn work(&mut self) {
let new_self = modify(MyStruct {
counter: self.counter,
});
*self = modify(new_self);
}
or by adding the Clone
and/or Copy
trait:
#[derive(Clone)]
struct MyStruct {
pub counter: i32,
}
// ...
fn work(&mut self) {
let new_self = modify(self.clone());
*self = modify(new_self);
}
or
#[derive(Clone, Copy)]
struct MyStruct {
pub counter: i32,
}
// ...
fn work(&mut self) {
*self = modify(*self);
*self = modify(*self);
}
The arguably more sane thing to do here though is to change modify
into taking &mut MyStruct
itself, too, i.e.
struct MyStruct {
pub counter: i32,
}
fn modify (my_struct: &mut MyStruct) {
my_struct.counter += 1;
}
impl MyStruct {
fn work(&mut self) {
modify(self);
modify(self);
}
}
fn main() {
let mut my_struct = MyStruct {counter: 0};
my_struct.work();
}