Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Comment on lines 16 to +19
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing the formatting (and all the other things!)

It just occurred to me that the # syntax very likely is what prevents dprint from formatting this code block. It knows it's Rust from the info string and it knows to split on ,, but rustfmt will not be happy with the # lines.

Cc @gribozavr as another side-effect of #3101.

fn new() -> Vec<T>;
}

impl <T> Box<T> {
impl<T> Box<T> {
fn new(T) -> Box<T>;
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {}

Expand Down
18 changes: 12 additions & 6 deletions src/idiomatic/leveraging-the-type-system/newtype-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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>
Expand Down