What does "let MyStruct(x, y) = self;" do? And why can I access x and y?

Viewed 52

I was doing an exercise on exercism with Rust and came across a community solution where the implementation of the given user confused me.

pub struct Position(pub i16, pub i16);

impl Position {
    pub fn manhattan(&self) -> i16 {
        let Position(x, y) = self;
        x.abs() + y.abs()
    }
}

What I do not understand is:

let Position(x, y) = self;
x.abs() + y.abs()

As I understand it, I am initializing a struct of Position, but how am I initializing it without setting it equal to a variable? Further more, how am I then able to suddenly have access to the arguments?

1 Answers

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!

Related