Rust for the Novice Programmer/Pattern Matching

Pattern Matching

edit

So we have our AngleType enum that we created previously. Let's say we wanted to print out something explaining something about the angle. To change this based on the enum variant we encounter, we can use a match statement. For example:

 fn print_angle_info(points: Points) {
     match points.angle_type {
         AngleType::Acute => {
             println!("This angle is an acute angle; one of less than 90 degrees!");
         },
         AngleType::Right(axis_aligned) => {
             if axis_aligned {
                 println!("This angle is an axis-aligned right angle, exactly 90 degrees!");
             } else {
                 println!("This angle is a non axis-aligned right angle, exactly 90 degrees!");
             }
         },
         AngleType::Obtuse => {
             println!("This angle is an obtuse one. That means it more than 90 degrees, but less than 180.");
         },
         AngleType::Reflex => {
             println!("This angle is a reflex angle. That means it's very large: more than 180 degrees :O");
         }
     }
 }

If the angle type is the type described in each block, then we enter that one's braces {} and ignore all the other braces. For example, if we had a points where the angle_type was acute, we would only print out the acute angle section. Also notice that since the right angle has a boolean value attached, we can use a variable called axis_aligned which gets set to whatever that value is. We can then use it inside the {} braces to print more information about the angle. Note that we match all the variants of the enum. This is a requirement of the match statement. To demonstrate this, we can match against a number in the following function:

 fn print_specific_number_facts(num: u8) {
     match num {
         0 => println!("The number representing nothingness"),
         2 => println!("The only even prime number!"),
         6 => println!("A perfect number"),
     }
 }

First note I don't have the {} braces on the lines. This is since we only have a single line after matching a number, so we can leave them out, but we must have the comma at the end of the line. This won't compile, since it doesn't know what to do if we get a number not 1, 2 or 6. If we want it to do nothing on receiving a number that doesn't have a fact, we can explicitly say that like so:

 fn print_specific_number_facts(num: u8) {
     match num {
         0 => println!("The number representing nothingness"),
         2 => println!("The only even prime number!"),
         6 => println!("A perfect number"),
         _ => {},
     }
 }

The underscore acts as a match all, and the empty {} braces indicate we don't want to do anything in this case. We can use the underscore in other places as well. Remember our is_angle_right function from the last page? For reference, here's what it was before:

 fn is_angle_right(points: Points) -> bool {
     points.angle_type == AngleType::Right
 }

This isn't valid anymore, since we could have AngleType::Right(true) or AngleType::Right(false). Instead of individually matching against both of these, we can change it to:

 fn is_angle_right(points: Points) -> bool {
     points.angle_type == AngleType::Right(_)
 }

Also, what if we want to check do something only for a specific enum variant and use the inner value(s). A convenient way to describe this pattern is using 'if let'.

 fn print_right_angle(points: Points) {
     if let AngleType::Right(axis_aligned) = points.angle_type {
          if axis_aligned {
              println!("axis aligned right angle");
          } else {
              println!("non axis aligned right angle");
          }
     } else {
         println!("Some other angle");
     }
 }

This is very convenient for describing this pattern and quite easy to read as well. There are many more enum and struct patterns as well as many ways to match against those patterns but these are the basic building blocks to organise and structure data in Rust.

Next: Basics of References and Borrowing