diff --git a/training-slides/src/basic-types.md b/training-slides/src/basic-types.md index cf73da6..9097204 100644 --- a/training-slides/src/basic-types.md +++ b/training-slides/src/basic-types.md @@ -116,3 +116,7 @@ fn main() { let sub: &[i32] = &slice[0..1]; } ``` + +Note: + +- Use `.get()` method on the slice to avoid panics instead of accessing via index. \ No newline at end of file diff --git a/training-slides/src/collections.md b/training-slides/src/collections.md index 373cc8f..de7e7bb 100644 --- a/training-slides/src/collections.md +++ b/training-slides/src/collections.md @@ -380,16 +380,16 @@ Just sets the `V` type parameter to `()`! ## A Summary -| Type | Owns | Grow | Index | Slice | Cheap Insert | -| ------------ | :---: | :---: | :-----: | :---: | :----------: | -| Array | ✅ | ❌ | `usize` | ✅ | ❌ | -| Slice | ❌ | ❌ | `usize` | ✅ | ❌ | -| Vec | ✅ | ✅ | `usize` | ✅ | ↩ | -| String Slice | ❌ | ❌ | 🤔 | ✅ | ❌ | -| String | ✅ | ✅ | 🤔 | ✅ | ↩ | -| VecDeque | ✅ | ✅ | `usize` | 🤔 | ↪ / ↩ | -| HashMap | ✅ | ✅ | `T` | ❌ | ✅ | -| BTreeMap | ✅ | ✅ | `T` | ❌ | ✅ | +| Type | Owns | Grow | Index | Slice | Cheap Insert | +| :----------- | :--: | :--: | :-----: | :---: | :----------: | +| Array | ✅ | ❌ | `usize` | ✅ | ❌ | +| Slice | ❌ | ❌ | `usize` | ✅ | ❌ | +| Vec | ✅ | ✅ | `usize` | ✅ | ↩ | +| String Slice | ❌ | ❌ | 🤔 | ✅ | ❌ | +| String | ✅ | ✅ | 🤔 | ✅ | ↩ | +| VecDeque | ✅ | ✅ | `usize` | 🤔 | ↪ / ↩ | +| HashMap | ✅ | ✅ | `T` | ❌ | ✅ | +| BTreeMap | ✅ | ✅ | `T` | ❌ | ✅ | Note: diff --git a/training-slides/src/compound-types.md b/training-slides/src/compound-types.md index 0614f24..58c4e74 100644 --- a/training-slides/src/compound-types.md +++ b/training-slides/src/compound-types.md @@ -17,40 +17,46 @@ struct Point { - there is no partial initialization -```rust [1-4|6] +```rust [1-4|6-8] struct Point { x: i32, y: i32, } -let p = Point { x: 1, y: 2 }; +fn main() { + let p = Point { x: 1, y: 2 }; +} ``` ## Construction - but you can copy from an existing variable of the same type -```rust [7] +```rust [8] struct Point { x: i32, y: i32, } -let p = Point { x: 1, y: 2 }; -let q = Point { x: 4, ..p }; +fn main() { + let p = Point { x: 1, y: 2 }; + let q = Point { x: 4, ..p }; +} ``` ## Field Access -```rust [1-4|6|7-8] +```rust [1-4|7|8-9] struct Point { x: i32, y: i32, } -let p = Point { x: 1, y: 2 }; -println!("{}", p.x); -println!("{}", p.y); +fn main() { + let p = Point { x: 1, y: 2 }; + println!("{}", p.x); + println!("{}", p.y); +} ``` ## Tuples @@ -58,10 +64,12 @@ println!("{}", p.y); - Holds values of different types together. - Like an anonymous `struct`, with fields numbered 0, 1, etc. -```rust [1|2-3] -let p = (1, 2); -println!("{}", p.0); -println!("{}", p.1); +```rust [2|3-4] +fn main() { + let p = (1, 2); + println!("{}", p.0); + println!("{}", p.1); +} ``` ## `()` @@ -80,12 +88,14 @@ fn prints_but_returns_nothing(data: &str) -> () { - Like a `struct`, with fields numbered 0, 1, etc. -```rust [1|3|4-5] +```rust [1|4|5-6] struct Point(i32,i32); -let p = Point(1, 2); -println!("{}", p.0); -println!("{}", p.1); +fn main() { + let p = Point(1, 2); + println!("{}", p.0); + println!("{}", p.1); +} ``` ## Enums @@ -102,7 +112,7 @@ println!("{}", p.1); ## enum: Definition and Construction -```rust [1-6|8] +```rust [1-6|9] enum Shape { Square, Circle, @@ -110,12 +120,14 @@ enum Shape { Triangle, } -let shape = Shape::Rectangle; +fn main() { + let shape = Shape::Rectangle; +} ``` ## Enums with Values -```rust [1-6|2-4|5|8|9] +```rust [1-6|2-4|5|9|10] enum Movement { Right(i32), Left(i32), @@ -123,8 +135,10 @@ enum Movement { Down { speed: i32, excitement: u8 }, } -let movement = Movement::Left(12); -let movement = Movement::Down { speed: 12, excitement: 5 }; +fn main() { + let movement = Movement::Left(12); + let movement = Movement::Down { speed: 12, excitement: 5 }; +} ``` ## Enums with Values @@ -201,6 +215,10 @@ fn check_shape(shape: &Shape) { } ``` +Note: + +You might ask "Why is there a `*` in front of `radius` in `match`?" - It's because you only have an `&Shape` and so if you pattern match on a value of that type, you can only get a `&` reference to its contents. Unfortunately, the Rust operator `>` (or rather, the trait `std::cmp::PartialOrd` is implemented to compare two `i32` values, but not to compare an `i32` with an `&i32`. + ## Combining patterns - You can use the `|` operator to join patterns together diff --git a/training-slides/src/error-handling.md b/training-slides/src/error-handling.md index 5ca1bc1..b52329f 100644 --- a/training-slides/src/error-handling.md +++ b/training-slides/src/error-handling.md @@ -32,9 +32,11 @@ You can put anything in for the `E` in `Result`: fn literals() -> Result<(), &'static str> { Err("oh no") } + fn strings() -> Result<(), String> { Err(String::from("oh no")) } + fn enums() -> Result<(), Error> { Err(Error::BadThing) } @@ -191,3 +193,8 @@ fn main() -> Result<(), anyhow::Error> { Ok(()) } ``` + +Note: + +* Use `anyhow` if you do not care what error type your function returns, just that it captures something. +* Use `thiserror` if you must design your own error types but want easy `Error` trait impl. diff --git a/training-slides/src/generics.md b/training-slides/src/generics.md index 10050f7..8aa907e 100644 --- a/training-slides/src/generics.md +++ b/training-slides/src/generics.md @@ -59,6 +59,10 @@ fn print_stuff(value: X) { } ``` +Note: + +Default bounds are `Sized`, so finding the size of the type is one thing that you can do. You can also take a reference or a pointer to the value. + ## Generic Implementations ```rust [] @@ -228,6 +232,10 @@ impl AreaCalculator { } ``` +Note: + +Some types that cannot be written out, like the closure, can be expressed as return types using `impl`. e.g. `fn score(y: i32) -> impl Fn(i32) -> i32`. + ## Caution * Using Generics is *Hard Mode Rust* diff --git a/training-slides/src/good-design-practices.md b/training-slides/src/good-design-practices.md index 7fdb28e..e30faf5 100644 --- a/training-slides/src/good-design-practices.md +++ b/training-slides/src/good-design-practices.md @@ -64,8 +64,10 @@ mod tests { ```rust ignore struct Point(i32, i32); -let p = Point (1, 2); -assert_eq!(p, Point(1, 2)); +fn main() { + let p = Point (1, 2); + assert_eq!(p, Point(1, 2)); +} ``` Errors: @@ -80,8 +82,10 @@ Errors: #[derive(Debug, PartialEq)] struct Point(i32, i32); -let p = Point (1, 2); -assert_eq!(p, Point(1, 2)); +fn main() { + let p = Point (1, 2); + assert_eq!(p, Point(1, 2)); +} ``` ## `Debug` @@ -95,12 +99,12 @@ struct Point { x: i32, y: i32 } #[derive(Debug)] struct TuplePoint(i32, i32); -let p = Point { y: 2, x: 1 }; -let tp = TuplePoint (1, 2); -println!("{:?}", p); -// Point { x: 1, y: 2 } -println!("{:?}", tp); -// TuplePoint (1, 2) +fn main() { + let p = Point { y: 2, x: 1 }; + let tp = TuplePoint (1, 2); + println!("{:?}", p); // Point { x: 1, y: 2 } + println!("{:?}", tp); // TuplePoint (1, 2) +} ``` ## `PartialEq` diff --git a/training-slides/src/overview.md b/training-slides/src/overview.md index 29d46b5..7e84fcb 100644 --- a/training-slides/src/overview.md +++ b/training-slides/src/overview.md @@ -43,13 +43,18 @@ Rust is an empathic systems programming language that is determined to not let y ## Release Method - Nightly releases - - experimental features are only present on nighly releases - Every 6 weeks, the current nightly is promoted to beta - After 6 weeks of testing, beta becomes stable - Guaranteed backwards-compatibility - Makes small iterations easier +Note: + +- Cargo's "stabilization" section https://doc.crates.io/contrib/process/unstable.html#stabilization +- Crater tool +- Editions + ## Goals - Explicit over implicit @@ -75,13 +80,19 @@ spend time discussing the impact of many features on large projects. - Deallocation is automated - Warning: memory leaks are **safe** by that definition! +Note: + +- Memory safety: use-after-free, double-free +- Type safety, Thread safety +- Memory leaks: `Box::leak()` + ## Concurrent - "Concurrency without fear" - The type system detects concurrent access to data and requires synchronisation -- Also: Rust detects when unsynchronised access is safely possible! -- Protection from data races! +- Also: Rust detects when unsynchronised access is safely possible +- Protection from data races ## Fast diff --git a/training-slides/src/ownership.md b/training-slides/src/ownership.md index a4d455b..4382c27 100644 --- a/training-slides/src/ownership.md +++ b/training-slides/src/ownership.md @@ -11,7 +11,7 @@ Ownership is the basis for the memory management of Rust. - The owner is responsible for removing the data from memory - The owner always has full control over the data and can mutate it -## These Rules are: +## These Rules are - fundamental to Rust’s type system - enforced at compile time @@ -33,7 +33,7 @@ fn print_string(s: String) { Note: -The statement `let s = ...;` introduces a *variable binding* called `f` and gives it a *value* which is of type `String`. This distinction is important when it comes to transferring ownership. +The statement `let s = ...;` introduces a *variable binding* called `s` and gives it a *value* which is of type `String`. This distinction is important when it comes to transferring ownership. The function `String::from` is an associated function called `from` on the `String` type. @@ -212,6 +212,10 @@ Try adding more excitement by calling `add_excitement` multiple times. * *Mutably Borrowing* gives more permissions than *Borrowing* * *Owning* gives more permissions than *Mutably Borrowing* +Note: + +Why are there two types of Borrowed string types (`&String` and `&str`)? The first is a reference to a `struct` (`std::string::String`, specifically), and the latter is a built-in slice type which points at some bytes in memory which are valid UTF-8 encoded characters. + ## An aside: Method Calls * Rust supports *Method Calls*