From f7f6cc002c51a2d16984fddf09b6e1dd8aec2713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 2 Oct 2023 13:22:24 -0600 Subject: [PATCH 01/10] add OnceCell to shared-mutability --- training-slides/src/shared-mutability.md | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index 2c3ae75..ac76457 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -179,6 +179,29 @@ impl Post { } ``` +Note: + +An advanced trainee may ask about the use of `Splitting Borrows` which would allow one to borrow different fields with different mutability semantics, such as + +```rust +struct Foo { + a: i32, + b: i32, + c: i32, +} + +let mut x = Foo {a: 0, b: 0, c: 0}; +let a = &mut x.a; +let b = &mut x.b; +let c = &x.c; +*b += 1; +let c2 = &x.c; +*a += 10; +println!("{} {} {} {}", a, b, c, c2); +``` + +Which does work but doesn't solve the problem of declaring `x mut` to begin with. + ## `RefCell` A `RefCell` is also safe, but lets you *borrow* or *mutably borrow* the contents. @@ -252,3 +275,31 @@ To get *shared ownership* and *mutability* you need two things: * `Rc>` * (Multi-threaded programs might use `Arc>`) + +## `OnceCell` for special cases + +If you only need to modify a field *once*, a `OnceCell` can help you retain the ownership at compile-time + +```rust +fn main() { + let post = Post { + content: String::from("Blah"), + ..Post::default() + }; + assert!(post.days_on_hn_front_page.get().is_none()); + println!("{:?}", post.hn_ranking()); + assert!(post.days_on_hn_front_page.get().is_some()); +} + +#[derive(Debug, Default)] +struct Post { + content: String, + days_on_hn_front_page: std::cell::OnceCell, +} + +impl Post { + fn hn_ranking(&self) -> u64 { + self.days_on_hn_front_page.get_or_init(|| {7}) + } +} +``` From 0f021e58bb1b05eb9ffb44fa7864342fc184ff3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 2 Oct 2023 13:27:59 -0600 Subject: [PATCH 02/10] fix SUMMARY.md and phrasing in speaker notes --- training-slides/src/SUMMARY.md | 2 +- training-slides/src/shared-mutability.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/training-slides/src/SUMMARY.md b/training-slides/src/SUMMARY.md index 25560b4..e8d3aed 100644 --- a/training-slides/src/SUMMARY.md +++ b/training-slides/src/SUMMARY.md @@ -28,7 +28,7 @@ Using Rust on Windows/macOS/Linux. Requires [Rust Fundamentals](#rust-fundamenta - [Lifetimes](./lifetimes.md) - [Cargo Workspaces](./cargo-workspaces.md) - [Heap Allocation (Box and Rc)](./heap.md) -- [Shared Mutability (Cell, RefCell)](./shared-mutability.md) +- [Shared Mutability (Cell, RefCell, OnceCell)](./shared-mutability.md) - [Thread Safety (Send/Sync, Arc, Mutex)](./thread-safety.md) - [Closures and the Fn/FnOnce/FnMut traits](./closures.md) - [Spawning Threads and Scoped Threads](./spawning-threads.md) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index ac76457..b3c7840 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -200,7 +200,7 @@ let c2 = &x.c; println!("{} {} {} {}", a, b, c, c2); ``` -Which does work but doesn't solve the problem of declaring `x mut` to begin with. +Which does work but doesn't solve the problem of declaring `let mut x` to begin with. ## `RefCell` From f829e3feaa51ab2e7cf6f6f16adaa6d026198211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 2 Oct 2023 13:54:40 -0600 Subject: [PATCH 03/10] word choice for OnceCell explainer --- training-slides/src/shared-mutability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index b3c7840..6e835a4 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -278,7 +278,7 @@ To get *shared ownership* and *mutability* you need two things: ## `OnceCell` for special cases -If you only need to modify a field *once*, a `OnceCell` can help you retain the ownership at compile-time +If you only need to modify a field *once*, a `OnceCell` can help you keep the ownership system checks at compile-time ```rust fn main() { From 69771f0c3b8f41d0fc94389bfda265701380922e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 9 Oct 2023 11:12:41 -0600 Subject: [PATCH 04/10] Update SUMMARY.md --- training-slides/src/SUMMARY.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/training-slides/src/SUMMARY.md b/training-slides/src/SUMMARY.md index 58fb843..415cf04 100644 --- a/training-slides/src/SUMMARY.md +++ b/training-slides/src/SUMMARY.md @@ -22,16 +22,16 @@ Using Rust on Windows/macOS/Linux. Requires [Rust Fundamentals](#rust-fundamentals). -* [Methods and Traits](./methods-traits.md) -* [Rust I/O Traits](./io.md) -* [Generics](./generics.md) -* [Lifetimes](./lifetimes.md) -* [Cargo Workspaces](./cargo-workspaces.md) -* [Heap Allocation (Box and Rc)](./heap.md) -* [Shared Mutability (Cell, RefCell, OnceCell)](./shared-mutability.md) -* [Thread Safety (Send/Sync, Arc, Mutex)](./thread-safety.md) -* [Closures and the Fn/FnOnce/FnMut traits](./closures.md) -* [Spawning Threads and Scoped Threads](./spawning-threads.md) +* [Methods and Traits](./methods-traits.md) +* [Rust I/O Traits](./io.md) +* [Generics](./generics.md) +* [Lifetimes](./lifetimes.md) +* [Cargo Workspaces](./cargo-workspaces.md) +* [Heap Allocation (Box and Rc)](./heap.md) +* [Shared Mutability (Cell, RefCell, OnceCell)](./shared-mutability.md) +* [Thread Safety (Send/Sync, Arc, Mutex)](./thread-safety.md) +* [Closures and the Fn/FnOnce/FnMut traits](./closures.md) +* [Spawning Threads and Scoped Threads](./spawning-threads.md) # Advanced Rust From 2ed8a60f23f2bc82fd83896e04e5644659e65521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 9 Oct 2023 11:21:14 -0600 Subject: [PATCH 05/10] Update shared-mutability.md --- training-slides/src/shared-mutability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index 6e835a4..47d3cea 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -299,7 +299,7 @@ struct Post { impl Post { fn hn_ranking(&self) -> u64 { - self.days_on_hn_front_page.get_or_init(|| {7}) + *self.days_on_hn_front_page.get_or_init(|| {7}) } } ``` From eab19e2e1e358228416b9f8fbb8752964f45593f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 23 Oct 2023 09:23:20 -0600 Subject: [PATCH 06/10] change HN post to first_viewed_at --- training-slides/src/shared-mutability.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index 47d3cea..51ae16b 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -281,25 +281,27 @@ To get *shared ownership* and *mutability* you need two things: If you only need to modify a field *once*, a `OnceCell` can help you keep the ownership system checks at compile-time ```rust +use std::time::Instant; + fn main() { let post = Post { content: String::from("Blah"), ..Post::default() }; - assert!(post.days_on_hn_front_page.get().is_none()); + assert!(post.first_viewed_at.get().is_none()); println!("{:?}", post.hn_ranking()); - assert!(post.days_on_hn_front_page.get().is_some()); + assert!(post.first_viewed_at.get().is_some()); } #[derive(Debug, Default)] struct Post { content: String, - days_on_hn_front_page: std::cell::OnceCell, + first_viewed_at: std::cell::OnceCell, } impl Post { - fn hn_ranking(&self) -> u64 { - *self.days_on_hn_front_page.get_or_init(|| {7}) + fn hn_ranking(&self) -> Instant { + *self.first_viewed_at.get_or_init(|| {Instant::now()}) } } ``` From b97efc17e1d4d09fe592f0e596c2bb01ac6e77fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 23 Oct 2023 12:56:36 -0600 Subject: [PATCH 07/10] renaming is hard --- training-slides/src/shared-mutability.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index 51ae16b..db71f05 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -289,7 +289,7 @@ fn main() { ..Post::default() }; assert!(post.first_viewed_at.get().is_none()); - println!("{:?}", post.hn_ranking()); + println!("{:?}", post.date_of_first_view()); assert!(post.first_viewed_at.get().is_some()); } @@ -300,7 +300,7 @@ struct Post { } impl Post { - fn hn_ranking(&self) -> Instant { + fn date_of_first_view(&self) -> Instant { *self.first_viewed_at.get_or_init(|| {Instant::now()}) } } From 5757c8d9cbea20c3eeef75cae68a9e78f1486ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Tue, 5 Mar 2024 10:33:35 -0600 Subject: [PATCH 08/10] Update training-slides/src/shared-mutability.md Co-authored-by: Jonathan Pallant --- training-slides/src/shared-mutability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index db71f05..79767c5 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -301,7 +301,7 @@ struct Post { impl Post { fn date_of_first_view(&self) -> Instant { - *self.first_viewed_at.get_or_init(|| {Instant::now()}) + *self.first_viewed_at.get_or_init(Instant::now) } } ``` From ef202291f0e155aff9d62bb5881eec4dae223748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Tue, 5 Mar 2024 10:50:48 -0600 Subject: [PATCH 09/10] add note for future trainer audience in mind --- training-slides/src/shared-mutability.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index 79767c5..2b43467 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -181,7 +181,7 @@ impl Post { Note: -An advanced trainee may ask about the use of `Splitting Borrows` which would allow one to borrow different fields with different mutability semantics, such as +As an in-depth example of the borrowchecker's limitations, consider the [Splitting Borrows](https://doc.rust-lang.org/nomicon/borrow-splitting.html) idiom, which allows one to borrow different fields of the same struct with different mutability semantics: ```rust struct Foo { @@ -200,7 +200,7 @@ let c2 = &x.c; println!("{} {} {} {}", a, b, c, c2); ``` -Which does work but doesn't solve the problem of declaring `let mut x` to begin with. +The code works, but you *have* to use shadowing with `let a = &mut x.a;` or else the compiler will error. The borrowchecker is particularly frail here - replacing `Foo` with `x = [1,2,3]` and trying to borrow indexes will make it error out. ## `RefCell` From bd1d7745a53cc787416c39d8fea4b6cb9b42710c Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jul 2024 11:45:57 -0600 Subject: [PATCH 10/10] add LazyCell and LazyLock --- training-slides/src/shared-mutability.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/training-slides/src/shared-mutability.md b/training-slides/src/shared-mutability.md index 2b43467..99479ce 100644 --- a/training-slides/src/shared-mutability.md +++ b/training-slides/src/shared-mutability.md @@ -305,3 +305,5 @@ impl Post { } } ``` + +A [LazyCell](https://doc.rust-lang.org/std/cell/struct.LazyCell.html) will do this initialization lazily, and a [LazyLock](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) will do it in a threadsafe way.