You are mostly correct.
There is a neat trick to let the compiler tell you the type of variables rather than trying to infer them: let () = ...;
.
Using the Playground I get for the 1st case:
9 | let () = self;
| ^^ expected &Self, found ()
and for the 2nd case:
16 | let () = self;
| ^^ expected &Circle, found ()
The first case is actually special, because HasArea
is not a type, it's a trait.
So what is self
? It's nothing yet.
Said another way, it poses for any possible concrete type that may implement HasArea
. And thus the only guarantee we have about this trait is that it provides at least the interface of HasArea
.
The key point is that you can place additional bounds. For example you could say:
trait HasArea: Debug {
fn area(&self) -> f64;
}
And in this case, Self: HasArea + Debug
, meaning that self
provides both the interfaces of HasArea
and Debug
.
The second and third cases are much easier: we know the exact concrete type for which the HasArea
trait is implemented. It's Circle
.
Therefore, the type of self
in the fn area(&self)
method is &Circle
.
Note that if the type of the parameter is &Circle
then it follows that in all its uses in the method it is &Circle
. Rust has static typing (and no flow-dependent typing) so the type of a given binding does not change during its lifetime.
Things can get more complicated, however.
Imagine that you have two traits:
struct Segment(Point, Point);
impl Segment {
fn length(&self) -> f64;
}
trait Segmentify {
fn segmentify(&self) -> Vec<Segment>;
}
trait HasPerimeter {
fn has_perimeter(&self) -> f64;
}
Then, you can implement HasPerimeter
automatically for all shapes that can be broken down in a sequence of segments.
impl<T> HasPerimeter for T
where T: Segmentify
{
// Note: there is a "functional" implementation if you prefer
fn has_perimeter(&self) -> f64 {
let mut total = 0.0;
for s in self.segmentify() { total += s.length(); }
total
}
}
What is the type of self
here? It's &T
.
What's T
? Any type that implements Segmentify
.
And therefore, all we know about T
is that it implements Segmentify
and HasPerimeter
, and nothing else (we could not use println("{:?}", self);
because T
is not guaranteed to implement Debug
).
self
andSelf
. – Dahomeyself.radius
is slightly different than the question about itself (it's about ownership andCopy
). I suggest you edit it out to focus on whatself
is about here since that's the most difficult (and novel) part, whereas we already have plenty of questions about ownership. – Dahomey