fn main() {
if 1 == 2 { (1) (2)
println!("unlikely");
} else {
println!("expected");
}
}if
match and enums
for, while and loop loops
return and ?
iffn main() {
if 1 == 2 { (1) (2)
println!("unlikely");
} else {
println!("expected");
}
}| 1 | Paranthesis around the conditional are not necessary |
| 2 | Blocks need brackets, no shorthand |
matchfn main() {
let a = 4;
match a % 3 {
0 => { println!("divisible by 3") }, (1)
_ => { println!("not divisible by 3") }, (2)
}
}| 1 | match arm |
| 2 | default arm |
match and enumsenum Direction { (1)
North(i32), (2)
East(i32),
South(i32),
West(i32),
}
fn going_west(dir: &Direction) -> bool {
match dir { (3)
Direction::West(_) => true, (4)
_ => false
}
}| 1 | enum can take multiple forms |
| 2 | The forms are called "variants" and can carry data |
| 3 | Enums are inspected by matching … |
| 4 | … on the structure |
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}Option describes the possible absence of a value
Result describes that an operation might return an error instead
Option and Resultfn main() {
let will_overflow: Option<u8> = 10_u8.checked_add(250);
match will_overflow {
Some(sum) => println!("interesting: {}", sum),
None => eprintln!("addition overflow!"),
}
}Option and Resultuse std::fs::File;
use std::io;
fn main() {
let file_open: Result<File, io::Error> = File::open("Does not exist");
match file_open {
Ok(f) => println!("Success!"),
Err(e) => println!("Open failed: {:?}", e),
}
}fn main() {
let result: Option<u8> = 5_u8.checked_add(5);
match result {
Some(result) if result % 2 == 0 => println!("5+5 is even!"),
_ => println!("5+5 ... isn't even?"),
}
}Match guards allow further refining of a match
You can use the | operator to match several values in one arm.
enum Direction {
North(u32),
East(u32),
South(u32),
West(u32),
}
fn going_south_or_west(dir: &Direction) -> bool {
match dir {
Direction::West(_) | Direction::South(_) => true,
_ => false,
}
}if let conditionalsfn main() {
let maybe_arg = std::env::args().nth(2);
// can't know at compile time how many args are passed to our program
if let Some(arg) = maybe_arg {
println!("Got second command line argument: {}", arg);
}
}if let are idiomatic if only one case is of interest
loopfn main() {
let mut i = 0;
loop {
i += 1;
if i > 100 { break; }
}
}loop is used for (potentially) infinite loops
forfn main() {
let numbers = vec![1, 2, 3];
// `for item in iterable` creates an iterator by calling `iterable.into_iter()`
// and keeps calling `next() -> Option<Item>` on it until it receives `None`
for num in numbers {
println!("{}", num);
}
}for is used for iteration
whilefn main() {
let mut i = 0;
while !(i > 100) {
i += 1;
}
let mut iter = vec![1,2,3].into_iter();
while let Some(i) = iter.next() {
println!("number: {}", i);
}
}while is used for conditional loops
break, continue'outer: for i in 0..10 {
loop {
if i < 5 {
continue 'outer;
} else {
break 'outer;
}
}
}terminate current iteration or entire loop, using optional labels if not referring to innermost loop
returnfn get_number() -> u32 {
return 5;
8
}return can be used for early returns
The result of the last expression of a function is always returned
?use std::io;
use std::io::Read;
fn read_file(path: &std::path::Path) -> Result<String, io::Error> {
let mut f = std::fs::File::open(path)?;
let mut buffer = String::new();
f.read_to_string(&mut buffer)?;
Ok(buffer)
}? is "on error, early return"