What you see here is called destructuring, and is actually creating variables x
and y
from self
.
In this case, if you were to take this line of code:
let Position(x, y) = self;
What this is actually saying is equilalent to this:
let x = &self.0;
let y = &self.1;
You can think of the Position(x, y)
part of the assignment as a pattern. You will see pattern matching in many places in Rust, most commonly in match
statements. For example, unwrapping a variable of type Option<String>
might look like:
match some_optional_string {
Some(value) => println!("Got value: {value}"),
None => println!("Got no value")
};
Pattern matching also appears in conditions as well.
if let Some(value) = some_optional_string {
println!("Got value: {value}");
}
Another way to think of the particular example that you are referring to, is to replace self
with its actual value. Say for example self
was initialized with the values 3
and 4
, this might look like so:
let some_position = Position(3, 4);
Now if you were to call some_position.manhatten()
, self is equivalent to a &Position(3, 4)
If you replace all uses of self in the function with the value:
let Position(x, y) = &Position(3, 4);
x.abs() + y.abs()
(This won't actually compile because of lifetimes, but this is beside the point)
You can see more clearly now that Position(x, y)
matches &Position(3, 4)
but assigning x
and y
to &3
and &4
I saw you were also learning Haskell, which has a very similar concept. In haskell you could have a function that took in a Maybe
value, and have different definitions based on which it matched.
someFunc :: Maybe String -> String
someFunc (Just value) = value
someFunc Nothing = "I'm a different string"
Which has the same concept of destructuring or matching.
Hopefully this rambling makes sense!