website/content/slides/rust-introduction.md
2024-04-13 15:26:52 +02:00

702 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "Rust introduction"
date: 2023-02-10T13:51:58+01:00
---
# Introduction to...
![Rust](/static/slides/rust-introduction/rust.png)
-----
## Introduction
-----
### Types
---
#### Primitives
- signed ints: `i8`, `i16`, `i32`, `i64`, `i128`, `isize`
- unsigned ints: `u8`, `u16`, `u32`, `u64`, `u128`, `usize`
- floating point: `f32`, `f64`
- char: Unicode scalar
- bool: `true`, `false`
- unit: `()`
---
| Length | Signed | Unsigned |
| ------- | ------ | -------- |
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| arch | isize | usize |
---
#### Compound
| Name | Type | Example value |
| ----- | ----------- | ------------- |
| Array | `[3; i32]` | `[1, 2, 3]` |
| Tuple | `(i32, bool)` | `(1, true)` |
---
#### Enum
```rs
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
```
---
```rs
enum Coin {
Penny,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
```
---
#### Option & Result
```rs
enum Option<T> {
None,
Some(T),
}
enum Result<T, E> {
Ok(T),
Err(E),
}
```
---
#### Struct
```rs [1-4|6-10|12-19]
struct Container {
a: i32,
b: i32,
}
impl Container {
fn sum_ab(&self) -> i32 {
self.a + self.b
}
}
fn main() {
let container = Container {
a: 10,
b: 15,
};
let sum = container.sum_ab();
}
```
---
#### Vector
A contiguous growable array type, written as `Vec<T>`, short for vector.
---
```rs [1-3|5-6|8-9|11-12]
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
assert_eq!(vec.len(), 2);
assert_eq!(vec[0], 1);
assert_eq!(vec.pop(), Some(2));
assert_eq!(vec.len(), 1);
vec[0] = 7;
assert_eq!(vec[0], 7);
```
---
The `vec!` macro is provided for convenient initialization:
```rs
let mut vec1 = vec![1, 2, 3];
vec1.push(4);
let vec2 = Vec::from([1, 2, 3, 4]);
assert_eq!(vec1, vec2);
```
-----
### Control flow
---
#### if
```rs
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
```
---
```rs
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {number}");
}
```
---
#### loop
```rs
fn main() {
loop {
println!("again!");
}
}
```
---
```rs
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {result}");
}
```
---
### while
```rs
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}
```
---
### for
```rs
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {element}");
}
}
```
-----
### Iterators
JS:
```js
myList
.filter((t) => t !== "Filter me out")
.forEach(console.log);
```
Rust:
```rs
myList.into_iter()
.filter(|v| v != "Filter me out")
.for_each(|v| println!("{}", v));
```
---
| Funtion | What it does |
| ---------- | ------------ |
| .take(n) | reduces an iterator to its first n elements |
| .skip(n) | skips the first n elements |
| .cloned() | clones each element in the iterator |
| .enumerate | turns an iterator over elements t to an iterator over elements `(t: T, idx: usize)` |
| .cycle | loops the iterator infinitely |
| .rev | reverses an iterator |
-----
### Attributes
---
```rs [1-4|7-11|13-15|17-21]
// A function marked as a unit test
#[test]
fn test_foo() {
/* ... */
}
// A conditionally-compiled module
#[cfg(target_os = "linux")]
mod bar {
/* ... */
}
// A lint attribute used to suppress a warning/error
#[allow(non_camel_case_types)]
type int8_t = i8;
// Inline function, when compiling to machine code
#[inline(always)]
fn siema() {
/* ... */
}
```
-----
### Ownership
---
```rs [1-4|6-8|10-12|14-16|19-20|22-23|25-26|28-31]
struct Container {
a: i32,
b: i32,
}
fn consume(container: Container) -> i32 {
container.a + container.b
}
fn borrow(container: &Container) -> i32 {
container.a + container.b
}
fn borrow_mut(container: &mut Container) -> i32 {
container.a + container.b
}
fn main() {
let c1 = Container { a: 10, b: 15 };
let c2 = Container { ..c1 };
let sum1 = consume(c1);
// let sum1 = consume(c1); // illegal
let sum2 = borrow(&c2);
let sum2 = borrow(&c2); // ok
let mut c3 = Container { ..c2 };
let sum3 = borrow_mut(&mut c3);
let sum3 = borrow_mut(&mut c3); // ok
}
```
---
#### Borrow checker
![compiler error](/static/slides/rust-introduction/Screenshot_20230210_174448.png)
---
#### Mutable borrow
```rs [4|6-8|10-14|16-19]
fn main() {
// --snip--
let mut c3 = Container { ..c2 };
// ok
let ref1 = &c3;
let ref2 = &c3;
// ok
let ref1 = &mut c3;
ref1.a;
// compiler knows the first borrow can be dropped here
let ref2 = &mut c3;
// illegal
let ref1 = &mut c3;
let ref2 = &mut c3;
ref1.a;
}
```
---
#### Borrow checker
![compiler error](/static/slides/rust-introduction/Screenshot_20230210_175828.png)
-----
### Lifetimes
---
```rs [1-3|5-9|11-14]
fn test(arr: &[i32]) -> impl Iterator<Item=&i32> {
return arr.iter().cycle()
}
fn main() { // works
let x = vec![1, 2, 3];
let x = test(&x);
x.take(9).for_each(|i| print!("{i}") );
}
fn main() { // doesn't work
let x = test(&vec![1, 2, 3]);
x.take(9).for_each(|i| print!("{i}") );
}
```
---
![borrow cheecker](/static/slides/rust-introduction/Screenshot_20230222_180500.png)
---
```rs
fn borrow(c: &Container) {
// -- snip --
}
```
---
```rs
fn borrow<'a>(c: &'a Container) {
// -- snip --
}
```
---
```rs [1-17|4-8|11-15]
fn main() {
let i = 3; // Lifetime for `i` starts. ────────────────┐
// │
{ // │
let borrow1 = &i; // `borrow1` lifetime starts. ──┐│
// ││
println!("borrow1: {}", borrow1); // ││
} // `borrow1` ends. ─────────────────────────────────┘│
// │
// │
{ // │
let borrow2 = &i; // `borrow2` lifetime starts. ──┐│
// ││
println!("borrow2: {}", borrow2); // ││
} // `borrow2` ends. ─────────────────────────────────┘│
// │
} // Lifetime ends. ─────────────────────────────────────┘
```
---
```rs
struct Container<'a> {
x: &'a i32,
y: &'a i32,
}
```
---
```rs
fn main() {
let x = 1;
let v;
{
let y = 2;
let f = Container { x: &x, y: &y };
v = f.x;
}
println!("{}", *v);
}
```
---
![borrow checker](/static/slides/rust-introduction/Screenshot_20230221_184708.png)
---
```rs
struct Container<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
```
---
Day 21:
```rs
fn chain_eval<'data, 'a>(
start: &'data str,
dependees: &'a HashMap<&'data str, Vec<&'data str>>,
awaiting: &'a mut HashMap<&'data str, Shout>,
finished: &'a mut HashMap<&'data str, i64>,
) {
// -- snip --
}
```
---
```rs
pub fn run() -> () {
// 'data lifetime start
let data = utils::read_lines(utils::Source::Day(21));
let data = parse_data(&data);
println!("Day 21");
println!("Part 1: {}", solve1(&data));
println!("Part 2: {}", solve2(&data));
}
```
---
```rs [5-8|10-13]
fn test(arr: &[i32]) -> impl Iterator<Item=&i32> {
return arr.iter().cycle()
}
fn main() { // doesn't work
let x = test(&vec![1, 2, 3]);
x.take(9).for_each(|i| print!("{i}") );
}
fn main() { // works
let x = test(&[1, 2, 3]);
x.take(9).for_each(|i| print!("{i}") );
}
```
---
```rs [5-10]
fn test(arr: &[i32]) -> impl Iterator<Item=&i32> {
return arr.iter().cycle()
}
fn main() {
let x = {
let x: &'static [i32] = &[1, 2, 3];
test(&x)
};
x.take(9).for_each(|i| print!("{i}") );
}
```
-----
### Unsafe
---
```rs
unsafe fn test(n: i32) -> i32 {
// do something unsafe
n * 5
}
fn test2() -> i32 {
unsafe { test(4) }
}
fn main() {
let x = unsafe { test(4) };
let x = test2();
}
```
-----
### Traits
Haskell:
```haskell
class Cool a where
giveMeNumber :: a -> Integer
```
Rust:
```rs
trait Cool {
fn give_me_number(&self) -> i32;
}
```
---
Haskell:
```haskell
newtype Hello = Hello Integer
instance Cool Hello where
giveMeNumber (Hello n) = n * 10
```
Rust:
```rs
struct Hello(i32);
impl Cool for Hello {
fn give_me_number(&self) -> i32 { self.0 * 10 }
}
```
---
Haskell:
```haskell
main = do
let test = Hello 2
putStrLn $ show $ giveMeNumber test
```
Rust:
```rs
fn main() {
let test = Hello(2);
println!("{}", test.give_me_number())
}
```
---
#### Some built in traits:
- ToString
- Display and Debug
- Default
- From and Into
- Clone and Copy
- Deref
- Iterator and IntoIterator
---
#### Default
```rs
#[derive(Default, Debug)]
struct Hello { a: i32, b: i32, c: Vec<i32> }
fn main() {
let x = Hello { a: 22, ..Default::default() };
println!("{x:?}")
}
```
```
Hello { a: 22, b: 0, c: [] }
```
---
#### Sized
```rs
// core::marker::Sized
pub trait Sized { }
```
```rs
fn test() -> dyn Iterator<Item=i32> {
[1, 2, 3].into_iter()
}
```
![compiler](/static/slides/rust-introduction/Screenshot_20230223_191210.png)
---
```rs
fn test() -> Box<dyn Iterator<Item=i32>> + Sized {
Box::from([1, 2, 3].into_iter())
}
```
---
#### Send & Sync
- A type is Send if it is safe to send it to another thread.
- A type is Sync if it is safe to share between threads (T is Sync if and only if &T is Send).
-----
### ?
```rs
fn test() -> Option<i32> {
let mut test: Vec<i32> = vec![1, 2, 3];
let x = test.pop()?;
let y = test.pop()?.checked_add(22)?;
Some(x * y)
}
fn main() {
println!("{}", test().unwrap())
}
```