These are notes for a quick introduction to Rust.

Overview Link to heading

  • Statically typed & Compiled language.
  • Great developer experience:
    • cargo build system
    • rust-analyzer LSP

Rust features Link to heading

Basics Link to heading

C++Rust
std::size_tusize
std::pointerdiff_tisize
inti32
unsigned intu32
long longi64
unsigned long longu64
stringString
string_view&str
byteu8
charchar
vector<T>Vec<T>
array<int, 4>[u32; 4]
int[]&[u32]
TT
const T&&T
T&&mut T
T*unsafe { T* }
unique_ptr<T>Box<T>
optional<T>Option<T>
variant<T, E>Result<T, E>
C++Rust
for(int i = 0; i < n; ++i) {}for i in 0..n {}
while(true) {}loop {}
while(f()) {}while f() {}
do { } while (f());loop { if !f() { break; } }
switch x { case 1:; }match x { 1 => {} }
C++Rust
cout << “text” << endl;println!(“text”);
cout << 1+1 << endl;println!("{}", 1+1);
cout << n << endl;println!("{n}");

Basic syntax Link to heading

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// trailing return type
fn square(x: u32) -> u32 {
    // return on last line can be omitted
    x*x
}

// mutable reference
fn increment(x: &mut u32) {
    *x += 1;
}

fn main(){
    // Introduce variables with let.
    // Types are automatically inferred.
    let a = 1;
    // b is mutable.
    let mut b = 1;
    b += 1;
    // c, d, and e are usize:
    let c: usize = 1;
    let d = 1usize;
    let e = usize::MAX;

    // No parentheses needed.
    if a > b {
        // This shouldn't happen.
        panic!();
    }

    // 0..n is a `Range`.
    // Ranges are `IntoIterator` and converted into an iterator, which is looped over.
    for i in 0..n {
        // Print i to a line on stderr.
        eprintln!("{i}");
    }

    while b < 1000 {
        increment(b);
    }

    loop {
        b = square(b);
        if b > 1000000000 {
            break;
        }
    }

    // Pattern matching
    match 5 {
        0 => panic!(),
        1 => todo!(),
        2..3 => eprintln!("small"),
        x if x%2==0 => eprintln!("even {x}"),
        _ => eprintln!("odd");
    }
}

Expressions everywhere! Link to heading

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let a = { 1 + 1 };
let b = if a > 10 { a } else { 10 };
let c = loop {
    break 3;
};
let d = {
    let mut x = 1;
    while x < 1000 {
        x *= 2;
    }
    x
};
let a = match Some(5) {
    None => 0,
    Some(x) => 2*x,
};

Closures Link to heading

1
2
3
4
let double = |x| 2*x;
let a = double(1);
let multiply = |x: usize, y: usize| -> usize { x * y };
let b = multiply(2, 3);

Pattern matching Link to heading

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
let a: Option<i32> = Some(1);
match a {
    Some(0) => eprintln!("I am 0"),
    Some(x) if x % 2 == 0 => eprintln!("I am even"),
    Some(x) => eprintln!("I am {x}"),
    None => eprintln!("I am none"),
}

if let Some(x) = a {
    eprintln!("a = Some({x})");
}

let Some(x) = a else {
    return;
};
eprintln!("{x}");

let x = a.unwrap();

References Link to heading

Ownership Link to heading

Containers Link to heading

1
2
3
4
5
// Create an array
let a: [usize; 10] = [1; 10];
// Create a vec
let v: Vec<usize> = vec![1usize; 10];
assert_eq!(&a, &v, "Slices are not equal!");

Traits Link to heading

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
trait MyTrait {
    fn my_fn(&self);
}

impl MyTrait for usize {
    fn my_fn(&self) {
        eprintln!("I am a usize!");
    }
}

impl MyTrait for i32 {
    fn my_fn(&self) {
        eprintln!("I am a i32!");
    }
}

fn f(t: impl MyTrait) {
    t.my_fn();
}

fn main() {
    let a = 1; // i32 by default
    a.my_fn();
    let b = 1usize;
    b.my_fn();
    f(a);
    f(b);
}

Iterators Link to heading

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
for i in 0..10 {
    eprintln!("i={}", i);
}

let v = (0..10).collect_vec();

for x in &v {
    eprintln!("x={}", x);
}

for (i, x) in v.iter().enumerate() {
    eprintln!("{i:>2} => {x}");
}

for x in v.iter().filter(|x| **x % 2 == 0) {
    eprintln!("x={}", x);
}

for x in v.iter().map(|x| x * x) {
    eprintln!("square: {}", x);
}

Common libraries Link to heading

See blessed.rs for a list of commonly used and recommended libraries.

  • rand: random number generation.
  • clap: Command Line Argument Parsing.
  • serde: SERialization and Deserialization to json, yaml, and many other formats.
  • itertools: Extra utilities for iterating over stuff.
  • coloured: coloured terminal output.

Ecosystem Link to heading

  • Release cycle
  • Unstable rust
  • cargo {build,run} -r for release mode is much faster.
  • cargo add <crate> to add a dependency from CLI.
  • Many tools, like cargo flamegraph for profiling

There is a lot of high quality documentation:

First, rust-lang.org/learn contains a lot of useful links, some of which I replicate here:

The Rust book, doc.rust-lang.org/book
A gentle step by step introduction to the Rust language and ecosystem.

Check out the page on Control Flow and find something that you’ve not seen in other languages.

The reference, doc.rust-lang.org/reference
A more formal documentation of language features. Probably not so readable for beginners.

Find the page on Traits.

Documentation, doc.rust-lang.org/std
The standard library docs. Always keep this close by, and consider making a hotkey for searching it ;)

Read some of the docs for fn and println!.

Crate registry, crates.io
Where all public crates (packages) are. Useful for searching dependencies.

Try searching cli, and make sure to sort by All-Time Downloads. Find the github page and documentation of the first result.

Crate documentation, docs.rs
Documentation for all crates!

Search for serde and go to its docs. Find documentation for the Serialize trait. Is an array of length 64 serializable? Also you can find the corresponding crates.io page.

Comprehensive Rust, https://google.github.io/comprehensive-rust/
Introduction by Google

Hands-on Link to heading

Installation Link to heading

Go to https://rustup.rs and follow instructions.

  • Arch Linux alternatively has the rustup package.

Also install Rust analyzer binary, the LSP.

  • rust-analyzer vscode extension
  • rust-analyzer package in your package manager.
  • via rustup: rustup component add rust-analyzer

Make sure to enable the LSP in your IDE.

Recommended: install GitHub copilot as well.

Create a project Link to heading

Go to your projects folder, and run cargo new hello_world. This creates a new project:

1
2
3
4
5
6
7
8
9
> tree -a hello_world
hello_world
├── Cargo.lock
├── Cargo.toml
├── .git
│   └── ...
├── .gitignore
└── src
    └── main.rs

Hello, world! Link to heading

Currently, main.rs looks like this:

1
2
3
fn main() {
    println!("Hello, world!");
}
  • fn is the syntax to introduce a new function.
  • fn main creates the main function, the entry point of a binary.
  • main() takes no arguments.
  • println! is a macro (i.e. not a regular function call) that prints its argument to standard output.

To run the program, simply do cargo run from anywhere in the projects directory.

  • This will first build the code (see /target/), if not already done.
  • It will then run the binary.

Note that cargo is the package manager and build system (and more). cargo invokes rustc, the underlying compiler.

Small project ideas Link to heading

  • Compute all minimizers of a string.
  • Solve some Project Euler problems
  • Write a guessing game: the program chooses a random number and the user has to guess it with lower/correct/higher answers.