Course

First Contact

Most Rust syntax matches C++. Some code that compiles in C++ does not compile in Rust. The compiler errors look opaque at first. Later chapters explain why the compiler rejects these programs and how it detects the problems.

If you’ve written C++, most Rust syntax will feel familiar. Functions, structs, loops, and vectors work the same way.

1
2
3
fn main() {
    println!("Hello, world!");
}
1
2
3
4
5
fn main() {
    let x = 5;
    let y = 10;
    println!("{} + {} = {}", x, y, x + y);
}
1
2
3
4
5
6
7
8
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(3, 4);
    println!("Result: {}", result);
}
1
2
3
4
5
fn main() {
    let v = vec![1, 2, 3, 4, 5];
    println!("Vector: {:?}", v);
    println!("Length: {}", v.len());
}
1
2
3
4
5
6
7
8
9
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 10, y: 20 };
    println!("Point at ({}, {})", p.x, p.y);
}
1
2
3
4
5
6
7
fn main() {
    let mut count = 0;
    while count < 5 {
        println!("Count: {}", count);
        count += 1;
    }
}
Checkpoint

You see Rust syntax. You recognize it. You can read it.


These examples do not compile:

1
2
3
4
5
fn main() {
    let v = vec![1, 2, 3];
    let w = v;
    println!("{:?}", v);
}
1
error[E0382]: borrow of moved value: `v`
1
2
3
4
5
6
fn main() {
    let mut v = vec![1, 2, 3];
    let r = &v[0];
    v.push(4);
    println!("{}", r);
}
1
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
1
2
3
4
5
6
7
8
fn main() {
    let r;
    {
        let x = 5;
        r = &x;
    }
    println!("{}", r);
}
1
error[E0597]: `x` does not live long enough
1
2
3
4
5
6
fn main() {
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s;
    println!("{}, {}", r1, r2);
}
1
error[E0499]: cannot borrow `s` as mutable more than once at a time
1
2
3
4
5
6
7
8
fn get_string() -> &String {
    let s = String::from("hello");
    &s
}

fn main() {
    let s = get_string();
}
1
error[E0106]: missing lifetime specifier
1
2
3
4
5
6
fn main() {
    let mut v = vec![1, 2, 3];
    for x in &v {
        v.push(*x);
    }
}
1
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
1
2
3
4
5
6
fn main() {
    let mut v = vec![1, 2, 3];
    let closure = || v.push(4);
    println!("{:?}", v);
    closure();
}
1
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
1
2
3
4
5
6
7
8
use std::thread;

fn main() {
    let v = vec![1, 2, 3];
    thread::spawn(|| {
        println!("{:?}", v);
    });
}
1
error[E0373]: closure may outlive the current function
1
2
3
4
5
6
7
8
9
use std::rc::Rc;
use std::thread;

fn main() {
    let rc = Rc::new(5);
    thread::spawn(move || {
        println!("{}", rc);
    });
}
1
error[E0277]: `Rc<i32>` cannot be sent between threads safely

These programs contain memory bugs. Rust’s compiler rejects all of them. C++ compiles them.

Checkpoint

You see buggy code you recognize. You know these bugs. You see Rust rejects them. You do not know how Rust detects them.


The dangling reference case:

1
2
3
4
5
6
7
8
fn main() {
    let r;
    {
        let x = 5;
        r = &x;
    }
    println!("{}", r);
}

r refers to x. The block ends. x is gone. r points to nothing.

The C++ equivalent compiles and runs. It prints garbage, or crashes, or prints 5 by accident. The C++ standard leaves this behavior undefined.

1
2
3
4
5
6
7
8
int main() {
    int* r;
    {
        int x = 5;
        r = &x;
    }
    printf("%d\n", *r);
}
Checkpoint

You see one example explained in detail. You connect the Rust error to C++ undefined behavior. You know Rust catches bugs C++ misses. You do not know how yet. You are curious.