From 350141203aaa388bce2777064ea365942d8f1d4a Mon Sep 17 00:00:00 2001 From: Nicole L Date: Sun, 12 Apr 2026 02:12:32 -0700 Subject: [PATCH] Idiomatic: Fix various broken code examples (#3159) Many of the code snippets in Idiomatic are broken in various ways that make them either not run in the slides or not show the error the slide is intended to illustrate. This PR fixes a number of common issues: - **Invalid syntax**, e.g. `let` inside a struct definition. - **Missing `main` fn**. Not all examples need a `main`, but several code examples had code that only compiles if it's in a function, and so was failing to compile in the slides. Note that these were not caught in tests because [there seems to be a difference in behavior between how we test the code vs how the code works in the slides](https://github.com/google/comprehensive-rust/issues/3155). - **Missing `fn` keyword** on method definitions. In a few places I made more opinionated changes that I'm including here because splitting them out into a separate PR would just result in merge conflicts later: - `hash.md` - Removed the `friends` field. It wasn't necessary for demonstrating the `Hash` trait, and removing it allows the slide to be more concise. - `newtype_pattern.md` - Tweak the exercise to show that you can't pass an inner type where a newtype wrapper is expected (inverse of what it was previously demonstrating). I think this is a slightly clearer way to show what the slide is demonstrating. - A few minor tweaks to formatting. - Remove a few extraneous comments. - Add empty lines to help space things out and make them easier to read. - Remove `pub` keyword in a few places. - Remove the `#` prefix on lines that I think should be kept visible in the code snippet (though note that none of the hidden lines are ever hidden because the code snippets are editable, see https://github.com/google/comprehensive-rust/issues/2811). --- .../avoid-redundancy.md | 4 +-- .../predictable-api/common-traits/clone.md | 22 +++++++------ .../predictable-api/common-traits/copy.md | 9 +++++- .../predictable-api/common-traits/hash.md | 10 ++++-- .../common-traits/partialeq-eq.md | 10 +++--- .../predictable-api/naming-conventions/is.md | 8 ++--- .../predictable-api/naming-conventions/new.md | 5 ++- .../predictable-api/naming-conventions/to.md | 7 ++--- .../predictable-api/naming-conventions/try.md | 3 +- .../single-use-values.md | 3 ++ .../newtype-pattern.md | 18 +++++++---- .../newtype-pattern/semantic-confusion.md | 31 +++++++++++-------- 12 files changed, 81 insertions(+), 49 deletions(-) diff --git a/src/idiomatic/foundations-api-design/meaningful-doc-comments/avoid-redundancy.md b/src/idiomatic/foundations-api-design/meaningful-doc-comments/avoid-redundancy.md index 57cfa524..80382a25 100644 --- a/src/idiomatic/foundations-api-design/meaningful-doc-comments/avoid-redundancy.md +++ b/src/idiomatic/foundations-api-design/meaningful-doc-comments/avoid-redundancy.md @@ -22,8 +22,8 @@ fn parse_ip_addr_v4(input: &str) -> Option { ... } // Repeats information obvious from the field name. Can omit! struct BusinessAsset { - /// The customer id. - let customer_id: u64, + /// The customer id. + customer_id: u64, } // Mentions the type name first thing, don't do this! diff --git a/src/idiomatic/foundations-api-design/predictable-api/common-traits/clone.md b/src/idiomatic/foundations-api-design/predictable-api/common-traits/clone.md index c57ecf22..a0ebcc3a 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/common-traits/clone.md +++ b/src/idiomatic/foundations-api-design/predictable-api/common-traits/clone.md @@ -36,17 +36,21 @@ pub struct LotsOfData { set: BTreeSet, } -let lots_of_data = LotsOfData { - string: "String".to_string(), - vec: vec![1; 255], - set: BTreeSet::from_iter([1, 2, 3, 4, 5, 6, 7, 8]), -}; +fn main() { + let lots_of_data = LotsOfData { + string: "String".to_string(), + vec: vec![1; 255], + set: BTreeSet::from_iter([1, 2, 3, 4, 5, 6, 7, 8]), + }; -let lots_of_data_cloned = lots_of_data.clone(); + // Deep copy of all of the data in `lots_of_data`. + let lots_of_data_cloned = lots_of_data.clone(); -let reference_counted = Rc::new(lots_of_data); -// Copies the reference-counted pointer, not the value. -let reference_copied = reference_counted.clone(); + let reference_counted = Rc::new(lots_of_data); + + // Copies the reference-counted pointer, not the value. + let reference_copied = reference_counted.clone(); +} ```
diff --git a/src/idiomatic/foundations-api-design/predictable-api/common-traits/copy.md b/src/idiomatic/foundations-api-design/predictable-api/common-traits/copy.md index a8ba6ba7..ec13b94b 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/common-traits/copy.md +++ b/src/idiomatic/foundations-api-design/predictable-api/common-traits/copy.md @@ -22,8 +22,15 @@ When to implement: If possible, but with caveats. // Copy is just a marker trait with Clone as a supertrait. // pub trait Copy: Clone { } -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct Copyable(u8, u16, u32, u64); + +fn main() { + let copyable = Copyable(1, 2, 3, 4); + let copy = copyable; // Implicit copy operation + dbg!(copyable); + dbg!(copy); +} ```
diff --git a/src/idiomatic/foundations-api-design/predictable-api/common-traits/hash.md b/src/idiomatic/foundations-api-design/predictable-api/common-traits/hash.md index 23088679..cd022074 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/common-traits/hash.md +++ b/src/idiomatic/foundations-api-design/predictable-api/common-traits/hash.md @@ -29,12 +29,18 @@ When to implement: Almost always. // where H: Hasher, // Self: Sized { ... } // } +use std::collections::HashMap; -#[derive(Hash)] +#[derive(PartialEq, Eq, Hash)] pub struct User { id: u32, name: String, - friends: Vec, +} + +fn main() { + let user = User { id: 1, name: "Alice".into() }; + let mut map = HashMap::new(); + map.insert(user, "value"); } ``` diff --git a/src/idiomatic/foundations-api-design/predictable-api/common-traits/partialeq-eq.md b/src/idiomatic/foundations-api-design/predictable-api/common-traits/partialeq-eq.md index e4a092c2..adc5d057 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/common-traits/partialeq-eq.md +++ b/src/idiomatic/foundations-api-design/predictable-api/common-traits/partialeq-eq.md @@ -33,11 +33,13 @@ When to implement: Almost always. #[derive(PartialEq, Eq)] pub struct User { name: String, favorite_number: i32 } -let alice = User { name: "alice".to_string(), favorite_number: 1_000_042 }; -let bob = User { name: "bob".to_string(), favorite_number: 42 }; +fn main() { + let alice = User { name: "alice".to_string(), favorite_number: 1_000_042 }; + let bob = User { name: "bob".to_string(), favorite_number: 42 }; -dbg!(alice == alice); -dbg!(alice == bob); + dbg!(alice == alice); + dbg!(alice == bob); +} ```
diff --git a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/is.md b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/is.md index a9876fc1..bd199d77 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/is.md +++ b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/is.md @@ -15,16 +15,16 @@ Check a condition about a datatype. # // Copyright 2025 Google LLC # // SPDX-License-Identifier: Apache-2.0 # -impl Vec { - is_empty(&self) -> bool; +impl Vec { + fn is_empty(&self) -> bool; } impl f32 { - is_nan(self) -> bool; + fn is_nan(self) -> bool; } impl u32 { - is_power_of_two(self) -> bool; + fn is_power_of_two(self) -> bool; } ``` diff --git a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/new.md b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/new.md index 4cac2860..1f4ff082 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/new.md +++ b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/new.md @@ -16,12 +16,11 @@ method name. # // Copyright 2025 Google LLC # // SPDX-License-Identifier: Apache-2.0 # -impl Vec { - // Creates an empty vec. +impl Vec { fn new() -> Vec; } -impl Box { +impl Box { fn new(T) -> Box; } ``` diff --git a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/to.md b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/to.md index 1ceee11b..eeebc300 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/to.md +++ b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/to.md @@ -16,15 +16,14 @@ Prefix to a function that takes a borrowed value and creates an owned value # // SPDX-License-Identifier: Apache-2.0 # impl str { - // &str is not consumed. - fn to_owned(&str) -> String; + fn to_owned(&self) -> String; fn to_uppercase(&self) -> String; } impl u32 { - // take an owned self because `u32` implements `Copy` - to_be(self) -> u32; + // Take an owned self because `u32` implements `Copy` + fn to_be(self) -> u32; } ``` diff --git a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/try.md b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/try.md index 60270fcd..fee589c4 100644 --- a/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/try.md +++ b/src/idiomatic/foundations-api-design/predictable-api/naming-conventions/try.md @@ -17,11 +17,12 @@ Prefix for fallible methods that return a `Result`. # impl TryFrom for u32 { type Error = TryFromIntError; + fn try_from(value: i32) -> Result; } impl Receiver { - try_recv(&self) -> Result; + fn try_recv(&self) -> Result; } ``` diff --git a/src/idiomatic/leveraging-the-type-system/borrow-checker-invariants/single-use-values.md b/src/idiomatic/leveraging-the-type-system/borrow-checker-invariants/single-use-values.md index 8b17658c..97dcb48f 100644 --- a/src/idiomatic/leveraging-the-type-system/borrow-checker-invariants/single-use-values.md +++ b/src/idiomatic/leveraging-the-type-system/borrow-checker-invariants/single-use-values.md @@ -17,12 +17,15 @@ this is in cryptography: A "Nonce." # // SPDX-License-Identifier: Apache-2.0 # pub struct Key(/* specifics omitted */); + /// A single-use number suitable for cryptographic purposes. pub struct Nonce(u32); + /// A cryptographically sound random generator function. pub fn new_nonce() -> Nonce { Nonce(4) // chosen by a fair dice roll, https://xkcd.com/221/ } + /// Consume a nonce, but not the key or the data. pub fn encrypt(nonce: Nonce, key: &Key, data: &[u8]) {} diff --git a/src/idiomatic/leveraging-the-type-system/newtype-pattern.md b/src/idiomatic/leveraging-the-type-system/newtype-pattern.md index 91e4d998..dfd8fef0 100644 --- a/src/idiomatic/leveraging-the-type-system/newtype-pattern.md +++ b/src/idiomatic/leveraging-the-type-system/newtype-pattern.md @@ -25,12 +25,15 @@ Unlike type aliases, newtypes aren't interchangeable with the wrapped type: # // Copyright 2025 Google LLC # // SPDX-License-Identifier: Apache-2.0 # -# pub struct UserId(u64); -fn triple(n: u64) -> u64 { - n * 3 +pub struct UserId(u64); + +fn needs_user(user: UserId) { + // ... } -triple(UserId(1)); // 🛠️❌ +fn main() { + needs_user(1); // 🛠️❌ +} ``` The Rust compiler won't let you use methods or operators defined on the @@ -40,8 +43,11 @@ underlying type either: # // Copyright 2025 Google LLC # // SPDX-License-Identifier: Apache-2.0 # -# pub struct UserId(u64); -assert_ne!(UserId(1), UserId(2)); // 🛠️❌ +pub struct UserId(u64); + +fn main() { + assert_ne!(UserId(1), UserId(2)); // 🛠️❌ +} ```
diff --git a/src/idiomatic/leveraging-the-type-system/newtype-pattern/semantic-confusion.md b/src/idiomatic/leveraging-the-type-system/newtype-pattern/semantic-confusion.md index 18eb438e..17cae11e 100644 --- a/src/idiomatic/leveraging-the-type-system/newtype-pattern/semantic-confusion.md +++ b/src/idiomatic/leveraging-the-type-system/newtype-pattern/semantic-confusion.md @@ -17,16 +17,19 @@ unclear: # // SPDX-License-Identifier: Apache-2.0 # # struct LoginError; -pub fn login(username: &str, password: &str) -> Result<(), LoginError> { +fn login(username: &str, password: &str) -> Result<(), LoginError> { // [...] # Ok(()) } -# let password = "password"; -# let username = "username"; -// In another part of the codebase, we swap arguments by mistake. -// Bug (best case), security vulnerability (worst case) -login(password, username); +fn main() { + let password = "password"; + let username = "username"; + + // In another part of the codebase, we swap arguments by mistake. + // Bug (best case), security vulnerability (worst case) + login(password, username); +} ``` The newtype pattern can prevent this class of errors at compile time: @@ -35,18 +38,20 @@ The newtype pattern can prevent this class of errors at compile time: # // Copyright 2025 Google LLC # // SPDX-License-Identifier: Apache-2.0 # -pub struct Username(String); -pub struct Password(String); -# struct LoginError; +struct Username(String); +struct Password(String); +struct LoginError; -pub fn login(username: &Username, password: &Password) -> Result<(), LoginError> { +fn login(username: &Username, password: &Password) -> Result<(), LoginError> { // [...] # Ok(()) } -# let password = Password("password".into()); -# let username = Username("username".into()); -login(password, username); // 🛠️❌ +fn main() { + let password = Password("password".into()); + let username = Username("username".into()); + login(password, username); // 🛠️❌ +} ```