Destructuring
Like tuples, structs and enums can also be destructured by matching:
Structs
struct Foo { x: (u32, u32), y: u32, } #[rustfmt::skip] fn main() { let foo = Foo { x: (1, 2), y: 3 }; match foo { Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"), Foo { y: 2, x: i } => println!("y = 2, x = {i:?}"), Foo { y, .. } => println!("y = {y}, other fields were ignored"), } }
Enums
Patterns can also be used to bind variables to parts of your values. This is how
you inspect the structure of your types. Let us start with a simple enum type:
enum Result { Ok(i32), Err(String), } fn divide_in_two(n: i32) -> Result { if n % 2 == 0 { Result::Ok(n / 2) } else { Result::Err(format!("cannot divide {n} into two equal parts")) } } fn main() { let n = 100; match divide_in_two(n) { Result::Ok(half) => println!("{n} divided in two is {half}"), Result::Err(msg) => println!("sorry, an error happened: {msg}"), } }
Here we have used the arms to destructure the Result value. In the first
arm, half is bound to the value inside the Ok variant. In the second arm,
msg is bound to the error message.
This slide should take about 8 minutes.
Structs
- Change the literal values in
footo match with the other patterns. - Add a new field to
Fooand make changes to the pattern as needed. - The distinction between a capture and a constant expression can be hard to
spot. Try changing the
2in the second arm to a variable, and see that it subtly doesn’t work. Change it to aconstand see it working again.
Enums
Key points:
- The
if/elseexpression is returning an enum that is later unpacked with amatch. - You can try adding a third variant to the enum definition and displaying the errors when running the code. Point out the places where your code is now inexhaustive and how the compiler tries to give you hints.
- The values in the enum variants can only be accessed after being pattern matched.
- Demonstrate what happens when the search is inexhaustive. Note the advantage the Rust compiler provides by confirming when all cases are handled.
- Save the result of
divide_in_twoin theresultvariable andmatchit in a loop. That won’t compile becausemsgis consumed when matched. To fix it, match&resultinstead ofresult. That will makemsga reference so it won’t be consumed. This “match ergonomics” appeared in Rust 2018. If you want to support older Rust, replacemsgwithref msgin the pattern.