Skip to content
Open
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 @@ -9,34 +9,21 @@ SPDX-License-Identifier: CC-BY-4.0

# `as_` and `_ref`: reference conversions

`as` is a prefix for methods that convert references. `ref` is a suffix (but
prefer `as`.)

`as` methods borrow out the primary piece of data contained in `&self`.

Most commonly return references, but can also return a custom borrowing type or
an unsafe pointer.

```rust,compile_fail,editable
# // Copyright 2025 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
impl<T> Rc<T> {
fn as_ptr(&self) -> *const T;

// Very common on container types, see how it's also on Option.
fn as_ref(&self) -> &T;

fn as_ptr(&self) -> *const T;
}

impl<T> Option<T> {
fn as_ref(&self) -> Option<&T>;
// Slices can be empty! So this is 0 or 1 elements.
fn as_slice(&self) -> &[T];
}

impl OwnedFd {
// Covered later.
fn as_fd(&'a self) -> BorrowedFd<'a>;
fn as_slice(&self) -> &[T];
}
```

Expand All @@ -47,46 +34,17 @@ impl OwnedFd {
- The borrowing relationship is most often straightforward: the return value is
a reference that borrows `self`.

- Borrowing can also be subtle, and merely implied.

- The returned value could be a custom borrowing type, fore example,
`BorrowedFd` borrows `OwnedFd` through an explicit lifetime.

- We cover custom borrowing types later in this deep dive,
[PhantomData: OwnedFd & BorrowedFd](../../../leveraging-the-type-system/borrow-checker-invariants/phantomdata-04-borrowedfd.md).

- The returned value could borrow `self` only logically, for example,
`as_ptr()` methods return an unsafe pointer. The borrow checker does not
track borrowing for pointers.
- The returned value could borrow `self` only logically, for example, `as_ptr()`
methods return an unsafe pointer. The borrow checker does not track borrowing
for pointers.

- The type implementing an "as" method should contain one primary piece of data
that is being borrowed out.

- The "as" naming convention does not work if the data type is an aggregate of
many fields without an obvious primary one. Think about the call site:

```rust,compile_fail
# // Copyright 2025 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
my_vec.as_ptr() // OK
my_person.as_first_name() // does not read right, don't use "as_"
my_person.first_name() // OK
```

- If you want to have two getters that you need to distinguish, one that
returns first name by value, and another one that returns it by reference,
use `_ref` suffix:
many fields without an obvious primary one.

```rust,compile_fail
# // Copyright 2025 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
impl Person {
fn first_name(&self) -> String
fn first_name_ref() -> &str
fn first_name_mut() -> &mut String
}
```
- If you have two reference getters that you need to distinguish, use the
`_ref` suffix.

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,27 @@ Component for methods that take a custom projection or comparison function.
# // SPDX-License-Identifier: Apache-2.0
#
impl<T> [T] {
// Simplified
fn sort_by(&mut self, compare: impl FnMut(&T, &T) -> Ordering);
fn sort(&mut self) where T: Ord;

// Uses a predicate to determine what items end up in non-overlapping chunks.
fn chunk_by_mut<F: FnMut(&T, &T) -> bool>(
&mut self,
pred: F,
) -> ChunkByMut<'_, T, F>;
}
fn sort_by(&mut self, compare: impl FnMut(&T, &T) -> Ordering);

trait Iterator {
// Provided method of Iterator. Simplified.
fn min_by<F>(
self,
compare: impl FnMut(&Self::Item, &Self::Item) -> Ordering,
) -> Option<Self::Item>;
fn sort_by_key<K, F>(&mut self, f: F)
where
F: FnMut(&T) -> K,
K: Ord;
}
```

<details>
- Method will take a comparison or projection function.

A projection function here being a function that, given a reference to a value
that exists in the data structure, will compute a value to perform the principle
computation with.

Methods like `sort_by_key` allow us to sort by _the hash function I've passed to
the method_ or sort by _this specific field of the data in the slice_.

For example, if you have a slice of values of some data structure you might want
to sort them by a field of that data structure, or even a hash value of that
data.

`sort_by` takes a comparator function directly.

- Most often seen in methods that sort or otherwise manipulate a slice with a
custom sort or comparison function rather than by the `Ord` implementation of
the type itself.
- `sort_by` takes a custom comparator function that replaces the normal `Ord`
comparison logic.

- Sometimes the "by" preposition is simply a preposition.
- `sort_by_key` takes a projection function that takes the original element and
returns an alternate value to use for sorting. This allow us to do things like
sort by a particular field of a struct.

"by", like some other name components, may end up in a method name for normal
linguistic reasons rather than holding specific naming convention semantic
weight.
- Sometimes the "by" preposition is simply a preposition:

- [`Read::by_ref()`](https://doc.rust-lang.org/std/io/trait.Read.html#method.by_ref)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,10 @@ A constructor function, strongly implying "type conversion".
# // Copyright 2025 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
impl CStr {
unsafe fn from_ptr<'a>(ptr: *const i8) -> &'a CStr;
}

impl Duration {
fn from_days(days: u64) -> Duration;
}

impl<T> Vec<T> {
fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Vec<T>;
}

impl i32 {
fn from_ascii(src: &[u8]) -> Result<i32, ParseIntError>;
}
Expand All @@ -37,31 +29,13 @@ impl u32 {
```

<details>

- Prefix for constructor-style, `From`-trait-style functions.

- These functions can take multiple arguments, but usually imply the user is
doing more of the work than a usual constructor would.

`new` is still preferred for most constructor-style functions, the implication
- `new` is still preferred for most constructor-style functions, the implication
for `from` is transformation of one data type to another.

- Ask: Without looking at the standard library documentation, what would the
argument type of `u32::from_be` be?

Answer guidance: we already see `u32::from_le_bytes` on the slide, it takes a
slice of bytes. So from_le must be simpler, taking not bytes. Think about the
contrast between `u32` and `be`. The argument must be a big-endian `u32`!

Follow-up question: How about `str::from_utf8`?

Answer guidance: `str` vs `utf8`. The argument can't be a `str` because every
`str` is valid UTF-8. So what is the simplest way to provide UTF-8 data? A
slice of bytes.

Follow-up: Why not `str::from_utf8_bytes`?

Answer: It could be in theory. However, the "omit needless words" principle
applies, the word "bytes" would merely repeat the obvious - could a UTF-8
sequence ever be non-bytes?

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,32 @@ SPDX-License-Identifier: CC-BY-4.0

# `into`

- Prefix for methods that convert `self` into another type.

Consumes `self`, returns an owned value.
Prefix for methods that convert `self` into another type. Consumes `self`,
returns an owned value.

```rust,compile_fail,editable
# // Copyright 2025 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
impl<T> Vec<T> {
fn into_parts(self) -> (NonNull<T>, usize, usize);
pub trait IntoIterator {
fn into_iter(self) -> Self::IntoIter;
}

impl<T> Cell<T> {
fn into_inner(self) -> T;
impl str {
fn into_string(self: Box<str>) -> String;
}
```

<details>
- Prefix for a function that consumes an owned value and transforms it into a value of another type.

Not reinterpret cast! The data can be rearranged, reallocated, changed in any
way, including losing information.
- Prefix for a function that consumes an owned value and transforms it into a
value of another type.

- corollary to `From`
- Not reinterpret cast! The data can be rearranged, reallocated, changed in any
way, including losing information.

- `into_iter` consumes a collection (like a vec, or a btreeset, or a hashmap)
and produces an iterator over owned values, unlike `iter` and `iter_mut` which
produce iterators over reference values.

- Ask the class: what will `Vec::into_raw_parts` do?

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ impl u32 {
```

<details>
- A boolean condition on a value.

- `is` prefix is preferred over methods with `not` in the name.
- A boolean condition on a value.

There are no instances of `is_not_` in standard library methods, just use
- `is` prefix is preferred over methods with `not` in the name. There are no
instances of `is_not_` in standard library methods, just use
`!value.is_[condition]`.

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@ Suffix for access-style methods.
# // SPDX-License-Identifier: Apache-2.0
#
impl<T> Vec<T> {
// Simplified
fn get(&self, index: usize) -> Option<&T>;
fn get_mut(&mut self, index: usize) -> Option<&mut T>;
}

impl<T> [T] {
// Simplified
fn iter(&self) -> impl Iterator<Item = &T>;
fn iter_mut(&mut self) -> impl Iterator<Item = &mut T>;
}

impl str {
fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error>;
}
```

<details>
- Mut for Mutability

- Suffix that signifies the method gives access to a mutable reference.

Requires mutable access to the value you're calling this method on.
- Requires mutable access to the value you're calling this method on.

- Rust can't abstract over mutability, so there's no way to write a method that
can be used both mutably and immutably. Instead, we write pairs of functions,
where the immutable version gets the shorter name and the mutable version gets
the `_mut` suffix.

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@ impl u32 {
```

<details>
- Methods that create a new owned value without consuming `self`, and imply a type conversion, are named starting with `to`.

- This is not a borrow checker escape hatch, or an instance of unsafe code. A
new value is created, the original data is left alone.
- Methods that create a new owned value without consuming `self`, and imply a
type conversion, are named starting with `to`.

- Methods that start with "to" return a different type, and strongly imply a
non-trivial type conversion, or even a data transformation. For example,
Expand All @@ -42,16 +41,17 @@ impl u32 {
call does not consume `self`.

- If you simply want to define a method that takes `&self` and returns an owned
value of the same type, implement the `Clone` trait.

Example: to_uppercase creates a version of a string with all uppercase letters.
value of the same type, implement [`Clone`] or [`ToOwned`].

- If you want to define a method that consumes the source value, use the "into"
naming pattern.

- Also seen in functions that convert the endianness of primitives, or copy and
expose the value of a newtype.

[`Clone`]: https://doc.rust-lang.org/stable/std/clone/trait.Clone.html
[`ToOwned`]: https://doc.rust-lang.org/stable/std/borrow/trait.ToOwned.html

## More to Explore

- Ask the class: What's the difference between `to_owned` and `into_owned`?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl<T> Receiver<T> {
```

<details>

- Prefix for methods that can fail, returning a `Result`.

- `TryFrom` is a `From`-like trait for types whose single-value constructors
Expand Down