From 0ffb3ad435b152f10c4125f63c517187c5a9673d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 1 Oct 2024 15:38:56 +0000 Subject: [PATCH] Add implementation-details segment --- src/SUMMARY.md | 4 ++ src/implementation-details.md | 3 + src/implementation-details/exercise.md | 5 ++ .../niche-optimization.md | 57 +++++++++++++++++++ src/implementation-details/solution.md | 1 + src/std-types/option.md | 4 +- src/user-defined-types/enums.md | 52 +---------------- 7 files changed, 75 insertions(+), 51 deletions(-) create mode 100644 src/implementation-details.md create mode 100644 src/implementation-details/exercise.md create mode 100644 src/implementation-details/niche-optimization.md create mode 100644 src/implementation-details/solution.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ff7b6239941..5e8427916a2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -162,6 +162,10 @@ - [Struct Lifetimes](lifetimes/struct-lifetimes.md) - [Exercise: Protobuf Parsing](lifetimes/exercise.md) - [Solution](lifetimes/solution.md) +- [Implementation Details](implementation-details.md) + - [Niche Optimization](implementation-details/niche-optimization.md) + - [Exercise: TBD](implementation-details/exercise.md) + - [Solution](implementation-details/solution.md) --- diff --git a/src/implementation-details.md b/src/implementation-details.md new file mode 100644 index 00000000000..305c9f860a9 --- /dev/null +++ b/src/implementation-details.md @@ -0,0 +1,3 @@ +# Implementation Details + +{{%segment outline}} diff --git a/src/implementation-details/exercise.md b/src/implementation-details/exercise.md new file mode 100644 index 00000000000..b22f082e24e --- /dev/null +++ b/src/implementation-details/exercise.md @@ -0,0 +1,5 @@ +# Exercise: TBD + +NOTES: + +- maybe a good place for a linked list? diff --git a/src/implementation-details/niche-optimization.md b/src/implementation-details/niche-optimization.md new file mode 100644 index 00000000000..470c94e25c0 --- /dev/null +++ b/src/implementation-details/niche-optimization.md @@ -0,0 +1,57 @@ +--- +minutes: 10 +--- + +# Niche Optimization + +For some types, there are in-memory bit patterns that do not represent a valid +value. For example, `bool` can only be 0 or 1, and references are represented as +non-NULL pointers. Rust uses this observation to store enums without a distinct +discriminant field, saving space. + +```rust,editable +#![allow(dead_code)] +use std::{mem::size_of_val, slice::from_raw_parts}; + +enum TriState { + Set(bool), + Unset, +} + +fn show(name: &str, value: T) { + let bytes = unsafe { + from_raw_parts(&value as *const T as *const u8, size_of_val(&value)) + } + .iter() + .map(|b| format!("{:02x}", b)) + .collect::>() + .join(""); + println!("{}: {} = {}", name, std::any::type_name::(), bytes); +} + +fn main() { + show("false", TriState::Set(false)); + show("true", TriState::Set(true)); + show("unset", TriState::Unset); +} +``` + +
+ +The example shows Rust choosing a non-boolean value for the `Unset` variant. + +Try showing: + +- `&x` for some x +- `Some(&x)` +- `None::<&u32>` +- `Some(Some(&x))` +- `std::num::NonZero::new(10)` + +Null pointer optimization: For +[some types](https://doc.rust-lang.org/std/option/#representation), Rust +guarantees that `size_of::()` equals `size_of::>()` and that the +all-zeroes pattern transmutes to `None`. This is a special-case of the niche +optimization for `Option`. + +
diff --git a/src/implementation-details/solution.md b/src/implementation-details/solution.md new file mode 100644 index 00000000000..a6700350abb --- /dev/null +++ b/src/implementation-details/solution.md @@ -0,0 +1 @@ +# Solution diff --git a/src/std-types/option.md b/src/std-types/option.md index a960dad7492..9d9e6380eab 100644 --- a/src/std-types/option.md +++ b/src/std-types/option.md @@ -30,7 +30,7 @@ fn main() { None. - It's common to `unwrap`/`expect` all over the place when hacking something together, but production code typically handles `None` in a nicer fashion. -- The niche optimization means that `Option` often has the same size in - memory as `T`. +- The [niche optimization](../implementation-details/niche-optimization.md) + means that `Option` often has the same size in memory as `T`. diff --git a/src/user-defined-types/enums.md b/src/user-defined-types/enums.md index dc34b80ffbf..72bace64ece 100644 --- a/src/user-defined-types/enums.md +++ b/src/user-defined-types/enums.md @@ -46,9 +46,9 @@ Key Points: - Rust uses minimal space to store the discriminant. - If necessary, it stores an integer of the smallest required size - If the allowed variant values do not cover all bit patterns, it will use - invalid bit patterns to encode the discriminant (the "niche optimization"). - For example, `Option<&u8>` stores either a pointer to an integer or `NULL` - for the `None` variant. + invalid bit patterns to encode the discriminant (the + "[niche optimization](../implementation-details/niche-optimization.md)", + discussed on day 3). - You can control the discriminant if needed (e.g., for compatibility with C): @@ -70,50 +70,4 @@ Key Points: Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2 bytes. -## More to Explore - -Rust has several optimizations it can employ to make enums take up less space. - -- Null pointer optimization: For - [some types](https://doc.rust-lang.org/std/option/#representation), Rust - guarantees that `size_of::()` equals `size_of::>()`. - - Example code if you want to show how the bitwise representation _may_ look - like in practice. It's important to note that the compiler provides no - guarantees regarding this representation, therefore this is totally unsafe. - - - ```rust,editable - use std::mem::transmute; - - macro_rules! dbg_bits { - ($e:expr, $bit_type:ty) => { - println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e)); - }; - } - - fn main() { - unsafe { - println!("bool:"); - dbg_bits!(false, u8); - dbg_bits!(true, u8); - - println!("Option:"); - dbg_bits!(None::, u8); - dbg_bits!(Some(false), u8); - dbg_bits!(Some(true), u8); - - println!("Option>:"); - dbg_bits!(Some(Some(false)), u8); - dbg_bits!(Some(Some(true)), u8); - dbg_bits!(Some(None::), u8); - dbg_bits!(None::>, u8); - - println!("Option<&i32>:"); - dbg_bits!(None::<&i32>, usize); - dbg_bits!(Some(&0i32), usize); - } - } - ``` -