Destructuring
A match
block can destructure items in a variety of ways.
Destructuring Tuples
Tuples can be destructured in a match as follows:
fn main() { let triple = (0, -2, 3); // TODO ^ Try different values for `triple` println!("Tell me about {:?}", triple); // Match can be used to destructure a tuple match triple { // Destructure the second and third elements (0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z), (1, ..) => println!("First is `1` and the rest doesn't matter"), (.., 2) => println!("last is `2` and the rest doesn't matter"), (3, .., 4) => println!("First is `3`, last is `4`, and the rest doesn't matter"), // `..` can be used to ignore the rest of the tuple _ => println!("It doesn't matter what they are"), // `_` means don't bind the value to a variable } }
Destructuring Arrays and Slices
Like tuples, arrays and slices can be destructured this way:
fn main() { // Try changing the values in the array, or make it a slice! let array = [1, -2, 6]; match array { // Binds the second and the third elements to the respective variables [0, second, third] => println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third), // Single values can be ignored with _ [1, _, third] => println!( "array[0] = 1, array[2] = {} and array[1] was ignored", third ), // You can also bind some and ignore the rest [-1, second, ..] => println!( "array[0] = -1, array[1] = {} and all the other ones were ignored", second ), // The code below would not compile // [-1, second] => ... // Or store them in another array/slice (the type depends on // that of the value that is being matched against) [3, second, tail @ ..] => println!( "array[0] = 3, array[1] = {} and the other elements were {:?}", second, tail ), // Combining these patterns, we can, for example, bind the first and // last values, and store the rest of them in a single array [first, middle @ .., last] => println!( "array[0] = {}, middle = {:?}, array[2] = {}", first, middle, last ), } }
Destructuring Enums
An enum is destructured similarly:
// `allow` required to silence warnings because only // one variant is used. #[allow(dead_code)] enum Color { // These 3 are specified solely by their name. Red, Blue, Green, // These likewise tie `u32` tuples to different names: color models. RGB(u32, u32, u32), HSV(u32, u32, u32), HSL(u32, u32, u32), CMY(u32, u32, u32), CMYK(u32, u32, u32, u32), } fn main() { let color = Color::RGB(122, 17, 40); // TODO ^ Try different variants for `color` println!("What color is it?"); // An `enum` can be destructured using a `match`. match color { Color::Red => println!("The color is Red!"), Color::Blue => println!("The color is Blue!"), Color::Green => println!("The color is Green!"), Color::RGB(r, g, b) => println!("Red: {}, green: {}, and blue: {}!", r, g, b), Color::HSV(h, s, v) => println!("Hue: {}, saturation: {}, value: {}!", h, s, v), Color::HSL(h, s, l) => println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l), Color::CMY(c, m, y) => println!("Cyan: {}, magenta: {}, yellow: {}!", c, m, y), Color::CMYK(c, m, y, k) => println!("Cyan: {}, magenta: {}, yellow: {}, key (black): {}!", c, m, y, k), // Don't need another arm because all variants have been examined } }
Destructuring Pointers/ref
For pointers, a distinction needs to be made between destructuring and dereferencing
as they are different concepts which are used
differently from languages like C/C++.
- Dereferencing uses
*
- Destructuring uses
&
,ref
, andref mut
Dereference v.s. Destructure
fn main() { // Assign a reference of type `i32`. The `&` signifies there // is a reference being assigned. let reference = &4; match reference { // If `reference` is pattern matched against `&val`, it results // in a comparison like: // `&i32` // `&val` // ^ We see that if the matching `&`s are dropped, then the `i32` // should be assigned to `val`. &val => println!("Got a value via destructuring: {:?}", val), } // To avoid the `&`, you dereference before matching. match *reference { val => println!("Got a value via dereferencing: {:?}", val), } // What if you don't start with a reference? `reference` was a `&` // because the right side was already a reference. This is not // a reference because the right side is not one. let _not_a_reference = 3; // Rust provides `ref` for exactly this purpose. It modifies the // assignment so that a reference is created for the element; this // reference is assigned. let ref _is_a_reference = 3; // Accordingly, by defining 2 values without references, references // can be retrieved via `ref` and `ref mut`. let value = 5; let mut mut_value = 6; // Use `ref` keyword to create a reference. match value { ref r => println!("Got a reference to a value: {:?}", r), } // Use `ref mut` similarly. match mut_value { ref mut m => { // Got a reference. Gotta dereference it before we can // add anything to it. *m += 10; println!("We added 10. `mut_value`: {:?}", m); }, } }
Destructuring Structures
Similarly, a struct can be destructured as shown:
fn main() { struct Foo { x: (u32, u32), y: u32, } // Try changing the values in the struct to see what happens let foo = Foo { x: (1, 2), y: 3 }; match foo { Foo { x: (1, b), y } => println!("First of x is 1, b = {}, y = {} ", b, y), // you can destructure structs and rename the variables, // the order is not important Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i), // and you can also ignore some variables: Foo { y, .. } => println!("y = {}, we don't care about x", y), // this will give an error: pattern does not mention field `x` //Foo { y } => println!("y = {}", y), } }
See also:
Arrays and Slices and Binding for @
sigil
#[allow(...)]
, color models and enum