1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2026-04-25 23:11:02 +02:00

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).
This commit is contained in:
Nicole L
2026-04-12 02:12:32 -07:00
committed by GitHub
parent b9e63dd122
commit 350141203a
12 changed files with 81 additions and 49 deletions
@@ -22,8 +22,8 @@ fn parse_ip_addr_v4(input: &str) -> Option<IpAddrV4> { ... }
// 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!
@@ -36,17 +36,21 @@ pub struct LotsOfData {
set: BTreeSet<u8>,
}
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();
}
```
<details>
@@ -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);
}
```
<details>
@@ -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<u32>,
}
fn main() {
let user = User { id: 1, name: "Alice".into() };
let mut map = HashMap::new();
map.insert(user, "value");
}
```
@@ -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);
}
```
<details>
@@ -15,16 +15,16 @@ Check a condition about a datatype.
# // Copyright 2025 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
impl <T> Vec<T> {
is_empty(&self) -> bool;
impl<T> Vec<T> {
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;
}
```
@@ -16,12 +16,11 @@ method name.
# // Copyright 2025 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
impl <T> Vec<T> {
// Creates an empty vec.
impl<T> Vec<T> {
fn new() -> Vec<T>;
}
impl <T> Box<T> {
impl<T> Box<T> {
fn new(T) -> Box<T>;
}
```
@@ -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;
}
```
@@ -17,11 +17,12 @@ Prefix for fallible methods that return a `Result`.
#
impl TryFrom<i32> for u32 {
type Error = TryFromIntError;
fn try_from(value: i32) -> Result<i64, TryFromIntError>;
}
impl<T> Receiver<T> {
try_recv(&self) -> Result<T, TryRecvError>;
fn try_recv(&self) -> Result<T, TryRecvError>;
}
```
@@ -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]) {}
@@ -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)); // 🛠️❌
}
```
<details>
@@ -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); // 🛠️❌
}
```
<details>