# Pattern matching

``````match SCRUTINEE {
PATTERN => EXPRESSION,
PATTERN => {
EXPRESSION BLOCK
}
}``````
``````let a = 1;

match a {
1 => println!("One"),
2 => println!("Two"),
i32::MIN..=i32::MAX => println!("Neither one nor two"),
}

// is equivalent to
if a == 1 {
println!("One");
} else if a == 2 {
println!("Two")
} else {
println!("Neither one nor two");
}``````

Example

``````let a = 1;

match a {
1 => println!("One"),
2 => println!("Two"),
}
``````

match is exhaustive!

``````let a = 1;

match a {
1 => println!("One"),
2 => println!("Two"),
i32::MIN..=i32::MAX => println!("Neither one nor two")
}
``````
``````let a = 1;

match a {
1 => println!("One"),
2 => println!("Two"),
_ => println!("Neither one nor two")
}
``````
``````let a = 1;

match a {
1 => println!("One"),
2 => println!("Two"),
.. => println!("Neither one nor two")
}
``````
``````let a = 51;

match a {
1 => println!("One"),
2 => println!("Two"),
n => println!("Neither one nor two, but {}", n)
}
``````

matching named variables

``````let a = 51;

match a {
1 => println!("One"),
2 => println!("Two"),
_ => println!("Neither one nor two, but {}", a)
}
``````

why not just print a directly?

``````let a = 51;

match a + 1 {
1 => println!("One"),
2 => println!("Two"),
n => println!("Neither one nor two, but {}", n)
}
``````
``````let a = 51;

match a + 1 {
1 => println!("One"),
2 => println!("Two"),
n => println!("Neither one nor two, but {}", n)
}

// is equivalent to
if a + 1 == 1 {
println!("One");
} else if a + 1 == 2 {
println!("Two");
} else {
println!("Neither one nor two, but {}", a + 1);
}``````
``````let a = 51;

match a {
1..5 => println!("one to four"),
5..=10 => println!("five to ten"),
1 | 2 | 3 | 4 => unreachable!(),
_ => println!("Anything else")
}``````

ranges, logical operators

``````match 3 {
1..=10 => println!("Between 1 and 10, but don't know exactly"),
_ => ()
}

match 3 {
n @ 1..=10 => println!("{} is between 1 and 10", n),
_ => ()
}``````

variable binding with @

``````match 3 {
1..=10 => println!("Between 1 and 10, but don't know exactly"),
_ => ()
}

match 3 {
n @ 1..=10 => println!("{} is between 1 and 10", n),
_ => ()
}``````
``````match 3 {
n if n >= 0 => println!("Positive number"),
n if n < 0 => println!("Negative number")
}
``````

match guards

``````match 3 {
n if n >= 0 => println!("Positive number"),
n if n < 0 => println!("Negative number"),
_ => ()
}
``````
``````match 3 {
n if n >= 0 => println!("Positive number"),
n if n < 0 => println!("Negative number"),
_ => unreachable!()
}
``````
``````let rgb = (255, 255, 255);

match rgb {
(255, _, _) => println!("Max red!"),
(_r, 255, _b) => println!("Max green!"),
(0, 0, b) => println!("Only blue, brightness: {:.2}", b as f64 / 255 as f64),
(r, g, b) => println!("r: {}, g: {}, b: {}", r, g, b),
}``````

Destructuring

``````// result from a video game speedrun attempt
enum SpeedRunResult {
Failed,
Aborted{
seconds: f64,
reason: String
},
Finished(f64),
}

match result {
SpeedRunResult::Failed => println!("Game over!"),
SpeedRunResult::Aborted{seconds: s, reason: r} => println!("Aborted at {}s because {}", s, r),
SpeedRunResult::Finished(seconds) => println!("{}s is an excellent time!", seconds),
}``````

Matching enums

``````// result from a video game speedrun attempt
enum SpeedRunResult {
Failed,
Aborted{
seconds: f64,
reason: String
},
Finished(f64),
}

let result = SpeedRunResult::Aborted{
seconds: 134.42,
reason: String::from("failed to jump over the koopa")
};

match result {
SpeedRunResult::Failed => println!("Game over!"),
SpeedRunResult::Aborted{seconds: s, reason: r} => println!("Aborted at {}s because {}", s, r),
SpeedRunResult::Finished(seconds) => println!("{}s is an excellent time!", seconds),
}``````

Matching enums

# if let / while let

``````let rgb = (255, 255, 255);

if let (255, _, _) = rgb {
println!("Max red!");
}

// is equivalent to
match rgb {
(255, _, _) => println!("Max red!"),
_ => (),
}
``````

### if let

``````while let Pattern = Scrutinee {
BlockExpression
}``````
``````let rgb = (255, 255, 255);

if let (255, _, _) = rgb {
println!("Max red!");
} else {
println!("Not max red..");
}

// is equivalent to
match rgb {
(255, _, _) => println!("Max red!"),
_ => println!("Not max red..")
}``````

else can be used with if let

``````while let Pattern = Scrutinee {
BlockExpression
}``````

while let

``````fn is_divisible_by(val: i64, div: i64) -> bool {
val / div == 2
}

let mut a = 1;

while let false = is_divisible_by(a, 7) {
a += 1;
}

println!("{}", a);``````

# Error handling

## panic!

• when a program reaches an unrecoverable state
• terminates the program and gives feedback
• unwinds the stack like C++
• there’s no way to recover *

## panic! example

``````fn main() {
panic!("Best program ever");
}``````

## panic! example

``````fn main() {
let _var = true || panic!("Best program ever");
}``````

## panic! example

``````fn main() {
let _var = true && panic!("Best program ever");
}``````

# Result enum

``````enum Result<T, E> {
Ok(T),
Err(E),
}``````
• std::result - included in global namespace
• Result, Ok(T), Err(e)
• ignoring Result gives compiler warning

## Handling errors

1. properly handle error
2. ignore error (panic on error)
3. propagate error up to the caller

### 1. Properly handle error

``````use std::fs::File;

fn main() {
let f = File::open("rust_workshop_notes.txt");

let f = match f {
Ok(file) => file,
Err(error) => panic!("Could not open file: {:?}", error),
};
}``````

### 2. Ignore error (panic on error)

``````use std::fs::File;

fn main() {
let f = File::open("rust_workshop_notes.txt");

let f = match f {
Ok(file) => file,
Err(error) => panic!("Could not open file: {:?}", error),
};
}``````
``````use std::fs::File;

fn main() {
let f = File::open("rust_workshop_notes.txt").unwrap();

/*let f = match f {
Ok(file) => file,
Err(error) => panic!("Could not open file: {:?}", error),
};*/
}``````
• unwrap (panic on error)
• expect (panic with message)
``````use std::fs::File;

fn main() {
let f = File::open("rust_workshop_notes.txt").expect("Could not open file");

/*let f = match f {
Ok(file) => file,
Err(error) => panic!("Could not open file: {:?}", error),
};*/
}

``````
``````fn main() {
let num_str = "one";
let fallback_num = 0;

let num: i32 = num_str.parse().unwrap_or(fallback_num);

println!("{}", num);
}
``````

### 3. Propagate error up to the caller

``````use std::num::ParseIntError;

type Point = (i32, i32);

fn parse_coordinate(x: &str, y: &str) -> Result<Point, ParseIntError> {
Ok( (x.parse::<i32>()?, y.parse::<i32>()?) )
}

fn main() {
let parsed: Point = parse_coordinate("1", "2").unwrap();

println!("{:?}", parsed);
}
``````

### 3. Propagate error up to the caller

``````use std::num::ParseIntError;

type Point = (i32, i32);

fn parse_coordinate(x: &str, y: &str) -> Result<Point, ParseIntError> {
Ok( (x.parse::<i32>()?, y.parse::<i32>()?) )
}

fn main() {
let parsed: Point = parse_coordinate("one", "two").unwrap();

println!("{:?}", parsed);
}
``````

### 3. Propagate error up to the caller

``````use std::num::ParseIntError;

type Point = (i32, i32);
type ParseResult<T> = Result<T, ParseIntError>;

fn parse_coordinate(x: &str, y: &str) -> ParseResult<Point> {
Ok( (x.parse::<i32>()?, y.parse::<i32>()?) )
}

fn main() {
let parsed: Point = parse_coordinate("one", "two").unwrap();

println!("{:?}", parsed);
}
``````

### 3. Propagate error up to the caller

``````use std::num::ParseIntError;

fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
match s.parse::<i32>() {
Ok(i) => Ok(i*2),
Err(e) => Err(e)
}
}

fn parse_and_double_2(s: &str) -> Result<i32, ParseIntError> {
Ok(s.parse::<i32>()? * 2)
}

fn main() {
println!("{:?}", parse_and_double("2"));
println!("{:?}", parse_and_double_2("2"));

println!("{:?}", parse_and_double_2("house"));
println!("{:?}", parse_and_double_2("2").unwrap());
}``````

### 3. Propagate error up to the caller

``````use std::num::ParseIntError;

fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
let res = match s.parse::<i32>() {
Ok(i) => Ok(i*2),
Err(e) => Err(e)
};

println!("Inside parse_and_double: {:?}", res);
res
}

fn parse_and_double_2(s: &str) -> Result<i32, ParseIntError> {
let res = Ok(s.parse::<i32>()? * 2);

println!("Inside parse_and_double_2: {:?}", res);
res
}

fn main() {
parse_and_double("2");
parse_and_double_2("2");

parse_and_double("house");
parse_and_double_2("house");
}``````

### 3. Propagate error up to the caller

``````use std::num::ParseIntError;

fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
let res = match s.parse::<i32>() {
Ok(i) => Ok(i*2),
Err(e) => Err(e)
};

println!("Inside parse_and_double: {:?}", res);
res
}

fn parse_and_double_2(s: &str) -> Result<i32, ParseIntError> {
let res = Ok(s.parse::<i32>()? * 2);

println!("Inside parse_and_double_2: {:?}", res);
res
}

fn main() {
parse_and_double("2");
parse_and_double_2("2");

parse_and_double("house");
parse_and_double_2("house");
}``````

### 3. Propagate error up to the caller

``````use std::num::ParseIntError;

fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
let res = match s.parse::<i32>() {
Ok(i) => i,
Err(e) => return Err(e)
};

let res = Ok(res*2);

println!("Inside parse_and_double: {:?}", res);
res
}

fn parse_and_double_2(s: &str) -> Result<i32, ParseIntError> {
let res = s.parse::<i32>()?;

let res = Ok(res*2);

println!("Inside parse_and_double_2: {:?}", res);
res
}

fn main() {
parse_and_double("2");
parse_and_double_2("2");

parse_and_double("house");
parse_and_double_2("house");
}``````

## Propagating multiple errors

1. collect errors using enum
2. box the error

### 1. Collect errors using enum

``````#[derive(PartialEq, Debug)]
enum ParseOrDoubleError {
ParseIntError,
DivideByZero,
}

fn parse_and_divide(s: &str) -> Result<i32, ParseOrDoubleError> {
let i = s.parse::<i32>();
match i {
Err(_e) => Err(ParseOrDoubleError::ParseIntError),
Ok(0) => Err(ParseOrDoubleError::DivideByZero),
Ok(non_zero) => Ok(1000 / non_zero)
}
}

fn main() {
println!("2: {:?}", parse_and_divide("2"));
println!("2: {:?}", parse_and_divide("0"));

println!("2: {:?}", parse_and_divide("house"));
}``````

### 2. Box the error

``````use std::fs::File;
use std::error::Error; // Error is a Trait

fn parse_and_open(number: &str, filepath: &str) -> Result<(), Box<dyn Error>> {
number.parse::<i32>()?;
File::open(filepath)?;
Ok(())
}

fn main() {
println!("{:?}", parse_and_open("2", "rust_workshop_notes.txt"));
println!("{:?}", parse_and_open("two", "rust_workshop_notes.txt"));
}``````

## Custom error

• Represents different errors with the same type
• Presents nice error messages to the user
• Is easy to compare with other types
• Good: Err(EmptyVec)
• Can hold information about the error
• Bad: Err("+ cannot be used here".to_owned())
• Composes well with other errors
`https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/define_error_type.html`

### Custom error

``````use std::fmt;
use std::error::Error;

#[derive(Debug, Clone)]
struct DivideByZeroError;

impl Error for DivideByZeroError {}

impl fmt::Display for DivideByZeroError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "divide by zero error")
}
}

fn parse_and_divide(s: &str) -> Result<i32, Box<dyn Error>> {
let i = s.parse::<i32>()?;
match i {
0 => Err(Box::new(DivideByZeroError)),
non_zero => Ok(1000 / non_zero)
}
}

fn main() {
println!("2: {:?}", parse_and_divide("2"));
println!("2: {:?}", parse_and_divide("0"));

println!("2: {:?}", parse_and_divide("house"));
}``````

### Custom error

``````use std::fmt;
use std::error::Error;

#[derive(Debug, Clone)]
struct DivideByZeroError;

impl Error for DivideByZeroError {}

impl fmt::Display for DivideByZeroError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "divide by zero error")
}
}

fn parse_and_divide(s: &str) -> Result<i32, Box<dyn Error>> {
let i = s.parse::<i32>()?;
match i {
0 => Err(Box::new(DivideByZeroError)),
non_zero => Ok(1000 / non_zero)
}
}

fn main() {
println!("2: {:?}", parse_and_divide("2"));
println!("2: {:?}", parse_and_divide("0"));

println!("2: {:?}", parse_and_divide("house"));
}``````

## crate anyhow

``````use anyhow::Error;

fn read_number_file(path: &str) -> Result<u32, Error> {
let number = string.parse()?;
Ok(number)
}

fn main() {
println!("Number from file: {}", number);
}
``````

## Applications vs Libraries

• Application
• Error types are not so important
• Errors will output to stdout/stderr
• crates: anyhow
• Library
• Used by other programs
• Errors are part of library API
• crates:

# Option enum

``````enum Option<T> {
Some(T),
None,
}``````
• std::option::Option - included in global namespace
• Option, Some(T), None
• ignoring Option gives compiler warning

## std::optional<T>

Rust C++
return None; return std::nullopt;
return Some(foo); return foo;
is_some() operator bool()
has_value()
unwrap() value()
unwrap_or(bar) value_or(bar)

## Handling errors options

1. properly handle error option
2. ignore error option (panic on error None)
3. propagate error None up to the caller

### 1. Properly handle option

``````struct Satelite {
distance_from_earth: Option<u32>
}

fn main() {
let sat = Satelite{distance_from_earth: Some(400)};

let in_orbit = match sat.distance_from_earth {
Some(d) if d <= 22_223 => true,
_ => false
};

println!("In orbit: {}", in_orbit);
}
``````

### 2. Ignore option (panic on None)

• unwrap (panic on None)
• expect (panic with message)
``````struct Satelite {
distance_from_earth: Option<u32>
}

fn main() {
let sat = Satelite{distance_from_earth: Some(400)};

let in_orbit = sat.distance_from_earth.unwrap() <= 22_223;

println!("In orbit: {}", in_orbit);
}
``````

### 2. Ignore option (panic on None)

• unwrap (panic on None)
• expect (panic with message)
``````struct Satelite {
distance_from_earth: Option<u32>
}

fn main() {
let sat = Satelite{distance_from_earth: None};

let in_orbit = sat.distance_from_earth.expect("Distance unknown") <= 22_223;

println!("In orbit: {}", in_orbit);
}
``````

### 3. Propagate None up to caller

``````fn adds_five(number: Option<u8>) -> Option<u8> {
}

fn main() {
let n: Option<u8> = Some(253);

println!("{:?}", n);
}
``````
``````fn adds_five(number: Option<u8>) -> Option<u8> {
}

fn main() {
let n: Option<u8> = None;

println!("{:?}", n);
}
``````

# Useful methods

• Result, Option -> boolean
• is_ok(), is_err()
• is_some(), is_none()
• Result -> Option
• ok(), err()
``````use std::fs::File;

fn main() {
let f = File::open("rust_workshop_notes.txt");

println!("f is_ok: {}", f.is_ok());
println!("f is_err: {}", f.is_err());

let option = f.ok();
println!("option: {:?}", option);

println!("option is_some: {}", option.is_some());
println!("option is_none: {}", option.is_none());
}
``````
``````// use std::fs::File;

fn main() {
// let f = File::open("rust_workshop_notes.txt");
let f: Result<&str, &str> = Ok("some data");

println!("f is_ok: {}", f.is_ok());
println!("f is_err: {}", f.is_err());

let option = f.ok();
println!("option: {:?}", option);

println!("option is_some: {}", option.is_some());
println!("option is_none: {}", option.is_none());
}
``````
``````use std::num::ParseIntError;

type Point = (i32, i32);

fn parse_coordinate(x: &str, y: &str) -> Result<Point, ParseIntError> {
Ok( (x.parse::<i32>()?, y.parse::<i32>()?) )
}

fn main() {
let parsed: Point = parse_coordinate("1", "2").unwrap();

println!("{:?}", parsed);
}
``````

Refactor parse_coordinate to return Option instead of Result

``````

type Point = (i32, i32);

fn parse_coordinate(x: &str, y: &str) -> Option<Point> {
Some( (x.parse::<i32>().ok()?, y.parse::<i32>().ok()?) )
}

fn main() {
let parsed: Point = parse_coordinate("1", "2").unwrap();

println!("{:?}", parsed);
}
``````

Refactor parse_coordinate to return Option instead of Result

#### Copy of Rust

By Hans Elias Bukholm Josephsen

• 183