Getting started!
Rustup, Cargo & Hello World
Rustup
Installer for the Rust toolchain
Let’s install Rust!
https://rustup.rs
Afterwards:
The cargo command should be runnable in your shell
$ cargo new hello_world
Created binary (application) `hello_world` package
$ cd testing
$ cargo run
Compiling hello_world v0.1.0 (/tmp/testing)
Finished dev [unoptimized + debuginfo] target(s) in 0.83s
Running `target/debug/hello_world`
Hello, world!
Creating and running our first program
fn main() {
println!("Hello, world!");
}
src/main.rs
The Rust docs are very useful:
fn main() {
for n in 0..10 {
let n_is_even = is_even(n);
if n_is_even {
println!("{} is even", n);
} else {
println!("{} is odd", n);
}
}
}
fn is_even(num: i32) -> bool {
num % 2 == 0
}
src/main.rs
0 is even
1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
fn main() {
for n in 0..10 {
let n_is_even = is_even(n);
if n_is_even {
println!("{} is even", n);
} else {
println!("{} is odd", n);
}
}
}
fn is_even(num: i32) -> bool {
num & 1 == 0
}
src/main.rs
Let's do a FizzBuzz
fn main() {
for n in 0..10 {
let n_is_even = is_even(n);
if n_is_even {
println!("{} is even", n);
// } else if <condition> {
} else {
println!("{} is odd", n);
}
}
}
fn is_even(num: i32) -> bool {
num % 2 == 0
}
If multiple of 3, print Fizz
If multiple of 5, print Buzz
If multiple of both print, FizzBuzz instead
Else, print the number
fn main() {
for n in 0..10 {
let is_mul_3 = n % 3 == 0;
let is_mul_5 = n % 5 == 0;
if is_mul_3 && is_mul_5 {
println!("FizzBuzz");
} else if is_mul_3 {
println!("Fizz");
} else if is_mul_5 {
println!("Buzz");
} else {
println!("{}", n);
}
}
}
fn main() {
for n in 0..10 {
match (n % 3, n % 5) {
(0, 0) => println!("FizzBuzz"),
(0, _) => println!("Fizz"),
(_, 0) => println!("Buzz"),
(_, _) => println!("{}", n),
}
}
}
fn main() {
for n in 0..10 {
let is_mul_3 = n % 3 == 0;
let is_mul_5 = n % 5 == 0;
if is_mul_3 && is_mul_5 {
println!("FizzBuzz");
} else if is_mul_3 {
println!("Fizz");
} else if is_mul_5 {
println!("Buzz");
} else {
println!("{}", n);
}
}
}
Intro Tour
- struct/class
- Methods
- enum
- unions
- copy
- Interfaces
- Inheritance
- Polymorphism
- Templates
- struct
- Associated Functions
- enum
- enum with fields
- move
- traits (ish)
- Nope! (traits ish)
- Polymorphism
- Generics
my_library_crate
- root module
- api
- data
- utils
my_binary_crate
- root module
- ui
Traits
→
→
Sortable
Hashable
implements Ord trait
implements Hash trait
Time
string
&string
let num_1 = 213;
let num_2: u8 = num_1;
Type Inference
TODO Concept Prototypes
- crates
- traits
- borrows/references/lifetimes
Ownership and Borrowing
Traits
Typing
FP Concepts
Safe Multithreading
Unsafe Blocks
Cargo
Basic Types
i8 / u8
i16 / u16
i32 / u32
i64 / u64
i128 / u128
isize / usize
f32
f64
Numeric primitive types
Explicitly sized
Pointer-sized
Floating point
let num = 13i32;
let num: i32 = 13;
let large_num = 32i64;
let smaller_num = large_num as i16;
assert_eq!(smaller_num, 32);
Other primitive types
bool
Boolean
()
Unit type
char
Unicode scalar
Composite Data Structures
Struct
/// Struct which represents a book in our
/// program.
struct Book {
name: String,
/// The ISBN number of the book, which
/// should be unique for each book.
isbn: u64,
}
Struct
/// Struct which represents a book in our
/// program.
struct Book {
name: String,
/// The ISBN number of the book, which
/// should be unique for each book.
isbn: u64,
}
struct Version(u32, u32, u32);
struct Service;
Struct
struct Book {
name: String,
isbn: u64,
}
struct Version(u32, u32, u32);
fn main() {
let book = Book {
name: String::from("Cryptonomicon")
isbn: 0380973464,
};
let version = Version(1, 8, 2)
}
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Counter {
count: 0,
}
}
fn increment(&mut self, inc: u32) {
self.count += inc;
}
}
fn main() {
let mut counter = Counter::new();
counter.increment(5);
}
struct Counter {
count: u32,
}
impl Counter {
const MAX: u32 = u32::MAX;
}
Tuple
let a: (u32, u8) = (1, 1);
struct A(u32, u32);
struct B(u32, u32);
fn my_func(a: A) {}
fn main() {
let val = B(12, 13);
my_func(val);
}
fn my_func(a: (u32, u32)) {}
fn main() {
let val = (12, 13);
my_func(val);
}
error[E0308]: mismatched types
--> src/main.rs:8:13
|
8 | my_func(val);
| ^^^ expected struct `A`, found struct `B`
()
(u32,)
(u32, u32)
(u32, u32, u32)
(u32, u32, u32, u32)
[...]
()
struct A;
struct A();
struct A(u32, u32);
(u32, u32)
by Structure
by Identity
Enum
enum BookKind {
Fantasy,
SciFi,
Action,
Mystery,
}
enum Publisher {
SelfPublished,
Company {
name: String,
org_no: u64,
},
}
enum Publisher {
SelfPublished,
Company {
name: String,
org_no: u64,
},
}
fn main() {
let variant_a = Publisher::SelfPublished;
let variant_b = Publisher::Company {
name: "Avon".into(),
org_no: 1128763212,
};
}
fn main() {
let publisher = [...];
match publisher {
Publisher::SelfPublished =>
println!("The book was self published"),
Publisher::Company { name, org_no } =>
println!(
"The book was published by {} ({})",
name, org_no
),
}
}
std::optional
The class template std::optional manages an optional contained value, i.e. a value that may or may not be present.
A common use case for optional is the return value of a function that may fail.
Given we had a way to do type parameters, could something like this be represented well with an enum?
enum
struct MyStruct<A, B> {
var_a: A,
var_b: A,
var_c: B,
}
Derives
#[derive(Copy, Clone)]
struct Counter {
num: u32,
step: u32,
}
Derives
#[derive(Debug)]
struct Counter {
num: u32,
step: u32,
}
let counter = Counter {
num: 31,
step: 1,
};
println!("my struct: {:?}");
my struct: Counter { num: 31, step: 1 }
- Ord/PartialOrd
- Eq/PartialEq
- Hash
- Default
- Serialize/Deserialize
Unions
Honorary Mention:
Control Flow
let num = 5;
if num > 10 {
println!("yay!");
} else {
println!("nay!");
}
let num = 5;
if num {
println!("yay!");
} else {
println!("nay!");
}
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if num {
| ^^^ expected `bool`, found integer
cargo run
let num = 5;
let string = if num > 10 {
"yay"
} else {
"nay"
};
println!("{}", string);
let num = 5;
match num {
4 => println!("four"),
5 => println!("five"),
_ => println!("other"),
}
let num = 5;
match num {
4 => println!("four"),
5 => println!("five"),
}
error[E0004]: non-exhaustive patterns: `i32::MIN..=3_i32` and
`6_i32..=i32::MAX` not covered
--> src/main.rs:5:7
|
5 | match num {
| ^^^ patterns `i32::MIN..=3_i32` and `6_i32..=i32::MAX`
not covered
|
= help: ensure that all possible cases are being handled,
possibly by adding wildcards or more match arms
= note: the matched value is of type `i32`
cargo run
struct MyData {
cond: bool,
num: i32,
}
enum MyEnum {
A {
data: MyData,
},
B {
num: i32,
},
}
fn main() {
let item = MyEnum::A {
data: MyData { cond: false },
};
match item {
MyEnum::A { data: MyData { cond: true, .. }} =>
println!("reticulate splines"),
MyEnum::B { num } =>
println!("rectify carborator #{}", num),
_ => println!("other"),
}
}
Pattern
Matching
Boolean
Conditions
match
if
if let
while
while let
enum MyEnum {
A { num: u32 },
B,
}
fn main() {
let value = A { num: 22 };
if let MyEnum::A { num } = value {
println!("Variant A: {}", num);
} else {
println!("Other variant");
}
}
while let SourceState::Item(item) = source.get_item() {
println!("has item");
}
while source.should_continue() {
println!("continuing");
}
let break_value = loop {
if let Some(val) = get_item() {
break val;
}
};
- Loops forever (until break'd)
- Same as
while true
for <item> in <iterator> {
<body>
}
- Works with Iterators
- For now, know it works with
- lists (vectors)
- ranges
- more
Option and Panics
Option enum
enum Option<T> {
Some(T),
None,
}
C++'s
std::optional<T>
Rust | C++ |
---|---|
return None; | return std::nullopt; |
return Some(foo); | return foo; |
is_some() | operator bool() has_value() |
unwrap() | value() |
unwrap_or(bar) | value_or(bar) |
fn maybe_get_number() -> Option<u32> { [...] }
match maybe_get_number() {
Some(num) => println!("Got number: {}", num),
None => println!("Didn't get anything!"),
}
1. Match on Option
fn maybe_get_number() -> Option<u32> { [...] }
fn my_fun() -> Option<String> {
let num = maybe_get_number()?;
Ok(format!("Got number: {}", num))
}
2. Propagate Option using ? operator
fn maybe_get_number() -> Option<u32> { [...] }
let num = maybe_get_number().unwrap();
println!("Got number: {}", num);
3. Panic on None using unwrap
Panic
fn main() {
panic!("something bad happened");
}
thread 'main' panicked at 'what is even going on', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
enum Result<T, E> {
Ok(T),
Err(E),
}
Also Result
Like option, but contains error variant
Sometimes unwrapped
Often handled, will talk about later
Modules and Visibility
- source file is compilation unit
- namespaces
- visibility
- crate is compilation unit
- modules
- visibility
mod a_module {
pub fn a_function() {}
}
use a_module::a_function;
fn main() {
a_function();
}
src/main.rs
mod a_module;
use a_module::a_function;
fn main() {
a_function();
}
src/main.rs
pub fn a_function() {}
src/a_module.rs
mod a_module;
use a_module::a_function;
fn main() {
a_function();
}
src/main.rs
pub fn a_function() {}
src/a_module/mod.rs
pub fn a_function() {}
src/a_module/nested.rs
src/a_module/mod.rs
pub mod nested;
src/a_module/mod.rs
mod a_module;
use a_module::nested::a_function;
pub struct Counter {
[...]
}
src/lib.rs
[dependencies]
# Depend on a crate on crates.io by version
rand = "0.8.4"
# Depend on a crate in a git repository
rand = { git = "https://github.com/rust-random/rand.git" }
# Depend on a local crate, by path
my_crate = { path = "../my_crate" }
Cargo.toml
Memory
Stack
Heap
Stack
Stack top
main
a
fn main() {
let a = 1;
let b = do_stuff(a);
}
fn do_stuff(m: u32) {
[...]
}
do_stuff
m
deep_fun
...
Stack
Stack top
main
a
fn main() {
let a = 1;
let b = do_stuff(a);
}
fn do_stuff(m: u32) {
[...]
}
do_stuff
m
Stack
Stack top
main
b, a
fn main() {
let a = 1;
let b = do_stuff(a);
}
fn do_stuff(m: u32) {
[...]
}
Stack
Heap
Heap
Stack top
main
do_stuff
malloc()
my
string
0xa2
free()
Stack
Stack top
main
a
fn main() {
let a = 1;
let b = do_stuff(a);
}
fn do_stuff(m: u32) {
[...]
}
do_stuff
m
deep_fun
...
Ownership
fn main() {
let a_string = String::from("Hello, world!");
println!("{:?}", a_string);
}
fn main() {
{
let a_string = String::from("Hello, world!");
println!("{:?}", a_string);
}
// Not gonna work!
// println!("{:?}", a_string);
}
fn main() {
let string_a = String::from("Hello, world!");
let string_b = string_a;
println!("{:?}", string_a);
println!("{:?}", string_b);
}
fn main() {
let string_a = String::from("Hello, world!");
let string_b = string_a;
println!("{:?}", string_a);
}
error[E0382]: borrow of moved value: `string_a`
--> src/main.rs:6:22
|
3 | let string_a = String::from("Hello, world!");
| -------- move occurs because `string_a` has type `String`,
| which does not implement the `Copy` trait
4 | let string_b = string_a;
| -------- value moved here
5 |
6 | println!("{:?}", string_a);
| ^^^^^^^^ value borrowed here after move
cargo run
- Each value in Rust has an owner
- There can only be a single owner at a time
- When the owner goes out of scope, the value will be dropped
The rules of the game
fn main() {
let string_a = String::from("Hello, world!");
let string_b = string_a;
println!("{:?}", string_a);
}
ptr | |
len | 13 |
capacity | 13 |
String
H |
e |
l |
l |
o |
, |
w |
o |
r |
l |
d |
! |
fn my_fun(string: String) {
println!("Hello, {}!", string);
}
fn main() {
let string = String::from("world");
my_fun(string);
println!("Hello again, {}!", string);
}
error[E0382]: borrow of moved value: `string_a`
--> src/main.rs:6:22
|
3 | let string_a = String::from("Hello, world!");
| -------- move occurs because `string_a` has type `String`,
| which does not implement the `Copy` trait
4 | let string_b = string_a;
| -------- value moved here
5 |
6 | println!("{:?}", string_a);
| ^^^^^^^^ value borrowed here after move
cargo run
fn my_fun(string: String) {
println!("Hello, {}!", string);
}
fn main() {
let string = String::from("world");
my_fun(string);
println!("Hello again, {}!", string);
}
cargo run
error[E0382]: borrow of moved value: `string`
--> src/main.rs:10:34
|
6 | let string = String::from("world");
| ------ move occurs because `string` has type `String`,
which does not implement the `Copy` trait
7 |
8 | my_fun(string);
| ------ value moved here
9 |
10 | println!("Hello again, {}!", string);
| ^^^^^^ value borrowed here
after move
Clone
Copy
- Performed implicitly by the compiler
- Performed by straight memcpy
- Performed explicitly by calling ------
- Can have custom implementation
.clone()
fn main() {
let string_a = String::from("Hello, world!");
let string_b = string_a;
println!("{:?}", string_a);
println!("{:?}", string_b);
}
error[E0382]: borrow of moved value: `string_a`
--> src/main.rs:6:22
|
3 | let string_a = String::from("Hello, world!");
| -------- move occurs because `string_a` has type `String`,
| which does not implement the `Copy` trait
4 | let string_b = string_a;
| -------- value moved here
5 |
6 | println!("{:?}", string_a);
| ^^^^^^^^ value borrowed here after move
cargo run
fn main() {
let string_a = String::from("Hello, world!");
let string_b = string_a.clone();
println!("{:?}", string_a);
println!("{:?}", string_b);
}
$ cargo run
"Hello, world!"
"Hello, world!"
cargo run
#[derive(Copy, Clone)]
struct MyStruct {
[...]
}
#[derive(Copy, Clone)]
struct MyStruct {
my_string: String,
my_integer: u32,
}
Borrowing & The Borrow Checker
The sequel of Ownership:
std::vector<int> myvector;
myvector.push_back(1);
int& num = myvector[0];
myvector.push_back(2);
std::cout << num << "\n";
let mut vec = Vec::new();
vec.push(1);
let num = &vec[0];
vec.push(2);
println!("{}", num);
std::vector<int> myvector;
myvector.push_back(1);
int& num = myvector[0];
myvector.push_back(2);
std::cout << num << "\n";
let mut vec = Vec::new();
vec.push(1);
let num = &vec[0];
vec.push(2);
println!("{}", num);
error[E0502]: cannot borrow `vec` as mutable because it
is also borrowed as immutable
--> src/main.rs:6:1
|
5 | let num = &vec[0];
| --- immutable borrow occurs here
6 | vec.push(2);
| ^^^^^^^^^^^ mutable borrow occurs here
7 |
8 | println!("{}", num);
| --- immutable borrow later used here
fn print_string(string: &String) {
println!("{}", string);
}
fn main() {
let my_string = String::from("hello");
print_string(&string);
print_string(&string);
}
fn append_newline(string: &String) {
string.push_str("\n");
}
fn main() {
let my_string = String::from("hello");
append_newline(&my_string);
println!("{:?}", my_string);
}
cargo run
error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&`
reference
--> src/main.rs:2:5
|
1 | fn append_newline(string: &String) {
| ------- help: consider changing this to be
a mutable reference: `&mut String`
2 | string.push_str("\n");
| ^^^^^^ `string` is a `&` reference, so the data it refers to
cannot be borrowed as mutable
fn append_newline(string: &String) {
string.push_str("\n");
}
fn main() {
let my_string = String::from("hello");
append_newline(&my_string);
println!("{:?}", my_string);
}
cargo run
error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&`
reference
--> src/main.rs:2:5
|
1 | fn append_newline(string: &String) {
| ------- help: consider changing this to be
a mutable reference: `&mut String`
2 | string.push_str("\n");
| ^^^^^^ `string` is a `&` reference, so the data it refers to
cannot be borrowed as mutable
&_
&mut _
- several may exist for a single piece of data
- may be copied
- may only be read
- only ONE may exist for any single piece of data
- may only be moved
- may be written or read
Mutually exclusive!
fn append_newline(string: &String) {
string.push_str("\n");
}
fn main() {
let my_string = String::from("hello");
append_newline(&my_string);
println!("{:?}", my_string);
}
cargo run
error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&`
reference
--> src/main.rs:2:5
|
1 | fn append_newline(string: &String) {
| ------- help: consider changing this to be
a mutable reference: `&mut String`
2 | string.push_str("\n");
| ^^^^^^ `string` is a `&` reference, so the data it refers to
cannot be borrowed as mutable
fn append_newline(string: &mut String) {
string.push_str("\n");
}
fn main() {
let my_string = String::from("hello");
append_newline(&my_string);
println!("{:?}", my_string);
}
cargo run
error[E0308]: mismatched types
--> src/main.rs:7:20
|
7 | append_newline(&my_string);
| ^^^^^^^^^^ types differ in mutability
|
= note: expected mutable reference `&mut String`
found reference `&String`
fn append_newline(string: &mut String) {
string.push_str("\n");
}
fn main() {
let my_string = String::from("hello");
append_newline(&mut my_string);
println!("{:?}", my_string);
}
cargo run
error[E0596]: cannot borrow `my_string` as mutable, as it is
not declared as mutable
--> src/main.rs:7:20
|
6 | let my_string = String::from("hello");
| --------- help: consider changing this to be
mutable: `mut my_string`
7 | append_newline(&mut my_string);
| ^^^^^^^^^^^^^^ cannot borrow as mutable
fn append_newline(string: &mut String) {
string.push_str("\n");
}
fn main() {
let mut my_string = String::from("hello");
append_newline(&mut my_string);
println!("{:?}", my_string);
}
cargo run
"hello\n"
fn main() {
let mut my_string = String::from("abc");
let ref_a = &mut my_string;
let ref_b = &my_string;
println!("{} {}", ref_a, ref_b);
}
cargo run
error[E0502]: cannot borrow `my_string` as immutable because
it is also borrowed as mutable
--> src/main.rs:5:17
|
4 | let ref_a = &mut my_string;
| -------------- mutable borrow occurs here
5 | let ref_b = &my_string;
| ^^^^^^^^^^ immutable borrow occurs here
6 |
7 | println!("{} {}", ref_a, ref_b);
| ----- mutable borrow later used here
cargo run
error[E0502]: cannot borrow `my_string` as immutable because
it is also borrowed as mutable
--> src/main.rs:5:17
|
4 | let ref_a = &mut my_string;
| -------------- mutable borrow occurs here
5 | let ref_b = &my_string;
| ^^^^^^^^^^ immutable borrow occurs here
6 |
7 | println!("{} {}", ref_a, ref_b);
| ----- mutable borrow later used here
fn main() {
let mut my_string = String::from("abc");
let ref_a = &mut my_string;
let ref_b = &my_string;
println!("{} {}", ref_a, ref_b);
}
fn main() {
let mut my_string = String::from("abc");
let ref_a = &mut my_string;
println!("{}", ref_a);
let ref_b = &my_string;
println!("{}", ref_b);
// ref_a and ref_b are both still in scope
}
$ cargo run
abc
abc
let mut a = 0;
let a_ref = &mut a;
*a_ref = 1;
println!(a);
Dereferencing
let a = 12;
let a_ref = &a;
let a2 = *a_ref;
Rust for C++ Developers - Itema Day 1
By Hans Elias Bukholm Josephsen
Rust for C++ Developers - Itema Day 1
- 249