Skip to content

Commit

Permalink
Add additional Rust slides
Browse files Browse the repository at this point in the history
  • Loading branch information
dtschan committed Sep 4, 2024
1 parent ce16784 commit 4b81210
Showing 1 changed file with 232 additions and 18 deletions.
250 changes: 232 additions & 18 deletions slides.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
### Dani Tschan<br>Mathis Hofer
<!-- .element style="margin-bottom: 12rem" --->


-*-*-

# Agenda
Expand Down Expand Up @@ -43,6 +42,7 @@ note:
* Wide field of application thanks to unsafe code and macros
* From embedded programming to web and desktop apps
* No undefined behaviour unlike C/C++
* Statically and strongly typed with inference

***

Expand Down Expand Up @@ -76,25 +76,27 @@ All variables must be initialized before use.

***

## Rust Syntax: String Types
## Rust Syntax: Notable String Types

* `String`:
* A growable, heap-allocated string.
* Used when you need to own and modify string data.
* Can be converted from and to slices (&str).
* Commonly seen in data structures.

***

## Rust Syntax: Notable String Types

* `&str` ("stir", "String Slice"):
* A non-owning reference to a sequence of UTF-8 encoded string data.
* Used for reading string data without taking ownership.
* Commonly seen as function parameters.
* String literals have type `&str`

* String: Heap-allocated string, guaranteed UTF-8
* &str: Slice of a string owned by other, guaranteed UTF-8
* String literals have type `&str`
* Data structures typically own Strings and use `String`
* Functions typically accept String references `&str`
***

## Rust Syntax: Notable String Types

```text
String
Expand All @@ -109,11 +111,111 @@ All variables must be initialized before use.

***

## Rust Syntax: Ownership and Moves
## Rust: Type Inference

* Rust types are inferred where possible
* Function arguments must always be specified
* To prevent implementation from changes affecting call sites
* Rust supports partial type inference:

```rust
let values = (1 .. 10).collect::<Vec<_>>();
```
<!-- .element class="very-big" --->

***

## Rust: Ownership and Moves

* Each value in Rust has an owner
* There can only be one owner at a time
* When the owner goes out of scope, the value will be dropped
* Ownership can be transferred between variables

***

## Rust: Ownership and Moves

```rust
let message = String::from("Hello");
let message2 = message; // Ownership of string transfered to message2
let message3 = message; // Error: use of moved value
```
<!-- .element class="very-big" --->

```rust
fn welcome(name: &str) -> String {
let result = format!("Hello {}", name);

result // Transfer ownership of string to caller
}

let message = welcome("John"); // `message` now owns the string
```
<!-- .element class="very-big" --->

***

## Rust: Copy types

* Values consisting of primitive types are copied on move operations
* The corresponding types implement the `Copy` trait

```rust
let number = 42; // 42 is of type i32, which is copy
let number2 = number; // value is copied, not moved
let number3 = number; // this works, value is copied again
```
<!-- .element class="very-big" --->

***

## Rust Syntax: References

Rust has two types of references:

* &T: Immutable, shared reference
* &mut T: Mutable, exclusive reference

***

## Rust: Borrowing

The use of references is govenered by borrowing rules:

* There can be either multiple immutable references to a value
* Or exactly one mutable reference
* A mutably referenced value can only be access through that reference
* A reference cannot outlive the referenced value

***

## Rust: Lifetimes

Every variable binding has a lifetime:
```
let r;
{
let i = 1;
r = &i;
}
println!("{}", r); // Error: `i` does not live long enough
```
<!-- .element class="very-big" style="margin-top: 0.5em;" --->

Rust sometimes needs explicit lifetime specifiers:

```rust
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
x
}
```
<!-- .element class="very-big" style="margin-top: 0.5em;" --->

***



## Rust Syntax: Tuples

Expand Down Expand Up @@ -300,7 +402,7 @@ let v2 = Vec2 { y: 2.0, x: 4.0 };

***

## Rust Syntax: Destructuring Structs
## Rust: Destructuring Structs

```rust
let v = Vec2 { x: 3.0, y: 6.0 };
Expand All @@ -318,9 +420,29 @@ let Vec2 { x, .. } = v;

***

## (Rust Syntax: Pattern Matching)
## Rust Syntax: Pattern Matching

```rust
struct Number {
odd: bool,
value: i32,
}

fn main() {
let one = Number { odd: true, value: 1 };
let two = Number { odd: false, value: 2 };
print_number(one);
print_number(two);
}

fn print_number(n: Number) {
match n {
Number { odd: true, value } => println!("Odd number: {}", value),
Number { odd: false, value } => println!("Even number: {}", value),
}
}
```
<!-- .element class="very-big" --->

***

Expand Down Expand Up @@ -440,16 +562,80 @@ let msg = Message::Move { x: 42, y: 0 };
<!-- .element class="very-big" --->

***

## Rust Syntax: Option

Option is an enum to represent optional values:

```rust
enum Option<T> {
None,
Some(T),
}
```
<!-- .element class="very-big" style="margin-top: 0.5em;" --->


```rust
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}

let result = divide(2.0, 3.0);

match result {
Some(x) => println!("Result: {x}"),
None => println!("Cannot divide by 0"),
}
```
<!-- .element class="very-big" --->


***
## Rust Syntax: Result
***

## Rust Syntax: Error Handling
Result is an enum used for returning errors:
```rust
enum Result<T, E> {
Ok(T),
Err(E),
}
```
<!-- .element class="very-big" style="margin-top: 0.5em;" --->

```rust
fn main() {
let number = env::args().nth(1).expect("Please provide a number");
match number.parse::<f64>() {
Ok(number) =>
println!("The square root of {} is {}", number, number.sqrt()),
Err(error) =>
println!("Can't parse '{}' as number: {}", number, error),
};
}
```
<!-- .element class="very-big" --->

***

## Rust Syntax: Iterators
## Rust: Loops and Iterators

Anything that is iterable can be used in a `for in` loop:

```rust
for i in (0..10).filter(|x| x % 2 == 0); {
println!("{}", i);
}
```

Which also supports `break` and `continue`.

[Iterators](https://doc.rust-lang.org/std/iter/trait.Iterator.html) provide various
methods for filtering and transforming.

***

Expand Down Expand Up @@ -577,7 +763,17 @@ Captures `hello` as `String` and moves it out of the closure on first call becau

***

## Rust Syntax: Moving Closures

To avoid lifetime issues closures can capture all variables by moving them:

```rust
let mut hello = String::from("Hello");
let mut msg = move || hello.clone() + " World";
```
<!-- .element class="very-big" --->

Captures `hello` as `String` instead of `&String` because of `move`.

***

Expand Down Expand Up @@ -658,17 +854,35 @@ async {

***

## Rust Syntax: Async

## Beginner Borrow Checker Tips

* Don't store references in structs and closures
* Use owned values instead, i.e. String, not &str
* Use `move` closures to capture values by move
* Clone values which are still neeeded before move
* Use `to_owned` to clone `&str` into `String`

***

## (Rust Syntax: Lifetimes)
## Beginner Borrow Checker Tips

***
* Use shadowing to clone before moving closure:

```rust
let hello = String::from("hello");

let msg = {
let hello = hello.clone();
move || hello + " World"
};

println!("{}", hello); // hello is still available here
```

<!-- .element class="very-big" --->

https://fasterthanli.me/articles/a-half-hour-to-learn-rust
* Syntactically this is a block returning a closure
* Shadowing like this is idiomatic in rust

-*-*-

Expand Down

0 comments on commit 4b81210

Please sign in to comment.