Day 1: Basic Rust, ownership and the borrow checker.
Basic Rust
-
rustc uses LLVM
-
Multiple architectures (WASM included)
cargo build --target wasm32-unknown-unknown
-
Basic rust syntax:
- Variables,
- Scalar and compound types,
- Enums,
- Structs References,
- Functions and Methods
-
Memory management:
- stack vs heap,
- manual memory management,
- scope based memory management and gc
-
Ownership:
- move semantics,
- copying and cloning,
- borrowing and lifetimes
Syntax sugar
- Blocks are delimited by curly braces
main
is the entry point- Provides hygienic macros and utf-8 encoded and contain unicode
Types
Scalar types
Types | Literals | |
---|---|---|
Signed integers | i8 , i16 , i32 , i64 , i128 , isize | -10 , 0 , 1_000 , 123i64 |
Unsigned integers | u8 , u16 , u32 , u64 , u128 , usize | 0 , 123 , 10u16 |
Floating point numbers | f32 , f64 | 3.14 , -10.0e20 , 2f32 |
Strings | &str | "foo" , r#"\\"# |
Unicode scalar values | char | 'a' , 'α' , '∞' |
Byte strings | &[u8] | b"abc" , br#" " "# |
Booleans | bool | true , false |
Compound Types
Types | Literals | |
---|---|---|
Arrays | [T; N] | [20, 30, 40] , [0; 3] |
Tuples | (T1, T2, T3, ...) | ('x', 1.2, 0) |
Other data structures exists:
Variables
mut
is used when a variable is initialized and needs to be changed later.
!
marks this as a macro invocation, not a function call.
let
statements declare local variables
fn function1() -> i32 { let _logical: bool = true; let _a_float: f64 = 1.0; let _an_integer = 5i32; let _default_int = 2; // i32 let mut mutablevar = 12; mutablevar = 35; return mutablevar; } fn main() { let out = function1(); println!("{}", out); }
Pointers
Similar to golang and pointers are treated with references and &
use rand::Rng; use std::cmp::Ordering; fn main() { let guess :i32 = 10; let secret_number :&i32 = &rand::thread_rng().gen_range(1..=10); match guess.cmp(secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } println!("{}", *secret_number) }
The main aim of lifetimes is to prevent dangling references, which cause a program to reference data other than the data it’s intended to reference.
fn main() { let ref_x: &i32; { let x: i32 = 10; ref_x = &x; } println!("ref_x: {ref_x}"); }
Won't compile because the value ref_x
is referring to has gone out of scope before we try to use it.
Slices
fn main() { let s = String::from("hello world"); let hello = &s[0..5]; let world = &s[6..s.len()]; println!("slices: {}, {}", hello, world) }
Functions and methods
- Functions are introduced with
fn
(pronounced "fun") - Methods comes from
structs
struct Square { width: i32 } impl Square { fn area(&self) -> i32 { return i32::pow(self.width, 2) } } fn main() { let sq :Square = Square{width: 10}; let area :i32 = sq.area(); println!("{area}"); }
Exercises
A few implicit conversions:
#![allow(unused)] fn main() { println!("{x} * {y} = {} -- {} {}", multiply(i16::from(x), y), y.to_string(), f64::from(y)); }
Array loops, matrices transposition.
fn transpose(mut matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] { // returning cloned transpose for now. requires research on borrowing concept. let mut new_matrix:[[i32; 3]; 3] = matrix.clone(); for (i, r) in matrix.iter().enumerate() { for (j, _c) in r.iter().enumerate() { new_matrix[i][j] = matrix[j][i]; } } return new_matrix } fn pretty_print(matrix: &[[i32; 3]; 3]) { println!("{:?}", matrix); } fn main() { let matrix = [ [101, 102, 103], // <-- the comment makes rustfmt add a newline [201, 202, 203], [301, 302, 303], ]; println!("matrix:"); pretty_print(&matrix); let transposed = transpose(matrix); println!("transposed:"); pretty_print(&transposed); }