When implementing a trait, we often use the keyword self
, a sample is as follows. I want to understand the representation of the many uses of self
in this code sample.
struct Circle {
x: f64,
y: f64,
radius: f64,
}
trait HasArea {
fn area(&self) -> f64; // first self: &self is equivalent to &HasArea
}
impl HasArea for Circle {
fn area(&self) -> f64 { //second self: &self is equivalent to &Circle
std::f64::consts::PI * (self.radius * self.radius) // third:self
}
}
My understanding is:
self
: &self
is equivalent to &HasArea
. self
: &self
is equivalent to &Circle
. self
representing Circle
? If so, if self.radius
was used twice, will that cause a move problem?Additionally, more examples to show the different usage of the self
keyword in varying context would be greatly appreciated.
You're mostly right.
The way I think of it is that in a method signature, self
is a shorthand:
impl S {
fn foo(self) {} // equivalent to fn foo(self: S)
fn foo(&self) {} // equivalent to fn foo(self: &S)
fn foo(&mut self) {} // equivalent to fn foo(self: &mut S)
}
It's not actually equivalent since self
is a keyword and there are some special rules (for example for lifetime elision), but it's pretty close.
Back to your example:
impl HasArea for Circle {
fn area(&self) -> f64 { // like fn area(self: &Circle) -> ...
std::f64::consts::PI * (self.radius * self.radius)
}
}
The self
in the body is of type &Circle
. You can't move out of a reference, so self.radius
can't be a move even once. In this case radius
implements Copy
, so it's just copied out instead of moved. If it were a more complex type which didn't implement Copy
then this would be an error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With