You've already forked comprehensive-rust
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:
@@ -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>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+3
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user