Basic Types & Variables
Boolean
-
bool
- Boolean (true
orfalse
)
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)