Basic Types & Variables
Boolean
-
bool- Boolean (trueorfalse)
Unsigned integers
-
u8,u16,u32,u64,u128
Signed integers
-
i8,i16,i32,i64,i128
Floating point numbers
-
f32,f64
Platform specific integers
-
usize- Unsigned integer. Same number of bits as the platform's pointer type. -
isize- Signed integer. Same number of bits as the platform's pointer type.
Characters and Strings
-
char- Unicode scalar value -
&str- String slice -
String- Owned string
Null / Nil
Rust doesn’t have the null feature that many other languages have. Null is a value that means there is no value there. Rust does have an enum that can encode the concept of a value being present or absent. This enum is Option<T>, and it is defined by the standard library as follows:
enum Option<T> {
Some(T),
None
}
You can use Some and None directly without the Option:: prefix. The Option<T> enum is still just a regular enum, and Some(T) and None are still variants of type Option<T>.
Tuple
let coordinates = (82, 64);
let score = ("Team A", 12);
Array & Slice
Slices are similar to arrays, but their length is not known at compile time. Instead, a slice is a two-word object; the first word is a pointer to the data, the second word is the length of the slice
let nums = [1, 2, 3, 4, 5];
let zeros = [0; 4]; // [0, 0, 0, 0]
let slice = &nums[1..4];
Hashmap
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Team1"), 900);
scores.entry("Team2".to_owned()).or_insert(250);
Struct
struct Person {
name: String,
is_active: bool,
}
let person1 = Person {
name: String::from("alex"),
is_active: false,
};
struct Point(i32, i32, i32);
let white = Point(255, 255, 255);
Enum
enum Action {
Stop,
Go { x: i32, y: i32 },
Yell(String),
ChangeColor(i32, i32, i32),
}
let act1 = Action::Stop;
let act2 = Action::Go { x: 10, y: 20 };
let act3 = Action::Yell("Hello".to_owned());
let act4 = Action::ChangeColor(255, 255, 0);
Constant
const MAX_SCORE: u32 = 10000;
Static variable
static APP_VERSION: u32 = 1;
static mut HIT_COUNT: u32 = 0;
Mutability
let mut val = 5;
val = 7;
Shadowing
let val = 5;
let val = val + 2;
Type alias type Score = u64;
Control Flow
if and if-let
let maybe_num = Some(7);
if maybe_num.is_some() {
println!("number is: {}", maybe_num.unwrap());
}
// `if let` allows various failure options to be specified:
if let Some(i) = letter {
println!("Matched {:?}!", i);
} else {
// Destructure failed. Change to the failure case.
println!("Didn't match a number. Let's go with a letter!");
}
let-else
With let-else, a refutable pattern can match and bind variables in the surrounding scope like a normal let, or else diverge (e.g. break, return, panic!) when the pattern doesn't match.
The scope of name bindings is the main thing that makes this different from match or if let-else expressions.
let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
panic!("Can't segment count item pair: '{s}'");
};
let Ok(count) = u64::from_str(count_str) else {
panic!("Can't parse integer: '{count_str}'");
};
while-let
while let can make awkward match sequences more tolerable.
let mut optional = Some(0);
// This reads: "while `let` destructures `optional` into
// `Some(i)`, evaluate the block (`{}`). Else `break`.
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
} // Doesn't require explicitly handling the failing case.
}
Loops
let mut counter = 0;
loop {
counter += 1;
if counter == 3 {
break; // Exit loop
}
}
let mut count = 0;
let result = loop {
count += 1;
if count == 5 {
break count; // Return value from loop
}
};
let mut num = 0;
while num < 50 {
num += 1;
}
Nested loops & labels
'outer: loop {
'inner: loop {
break; // This breaks the inner loop
break 'outer; // This breaks the outer loop
}
}
for loop
for n in 1..=101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
for loop takes ownership of v1_iter and makes it mutable behind the scenes.
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
for val in v1_iter {
println!("Got: {}", val);
}
Pattern Matching ❤️
Rust provides powerful pattern matching capabilities through the match expression and if let syntax, which can destructure, test, and compare values in a clean and concise way.
let x = 3;
match x {
1 => println!("one"),
2 | 3 => println!("two or three"), // multiple values
4..=9 => println!("within range"), // matching ranges
x => println!("{}", x), // matching named variables
_ => println!("default Case") // default case (ignores value)
}
// Another example:
let option_value = Some(5);
match option_value {
Some(value) => println!("The value is: {}", value),
None => println!("The value is missing"),
}
if let Some(value) = option_value { println!("The value is: {}", value); }
Destructuring
A match block can be used to destructure Tuples, Arrays/Slices, Enums, Pointers, Structs.
enum Color {
Red,
Blue,
Green,
RGB(u32, u32, u32),
CMYK(u32, u32, u32, u32),
}
fn main() {
let color = Color::RGB(122, 17, 40);
println!("What color is it?");
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::CMYK(c, m, y, k) =>
println!("Cyan: {}, magenta: {}, yellow: {}, key (black): {}!",
c, m, y, k),
...
}
}
Part 2 is coming soon.
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
),
...
}
More fun stuff - Destructuring examples
Binding
Indirectly accessing a variable makes it impossible to branch and use that variable without re-binding. match provides the @ sigil for binding values to names:
fn some_number() -> Option<u32> {
Some(42)
}
fn main() {
match some_number() {
// Got `Some` variant, match if its value, bound to `n`,
// is equal to 42.
Some(n @ 42) => println!("The Answer: {}!", n),
// Match any other number.
Some(n) => println!("Not interesting... {}", n),
// Match anything else (`None` variant).
_ => (),
}
}
Part 2 is coming soon!
Top comments (0)