Skip to content

Add Mechanics segment to the Unsafe Deep Dive #2827

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 58 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
470e7e0
First hint of a dedicated unsafe module appears
timClicks Jul 3, 2025
dfd5057
Use shorter working title
timClicks Jul 4, 2025
b828909
Reword the introduction
timClicks Jul 4, 2025
b4be7b9
Mention the unsafe deep dive in the notes for instructors
timClicks Jul 4, 2025
38da590
Use shorter working title
timClicks Jul 4, 2025
de10892
Sketch of opening
timClicks Jul 4, 2025
72e0739
Shorten comment so that it can fit on slides
timClicks Jul 8, 2025
7fdbb45
Add intrusive data structures
timClicks Jul 8, 2025
65d6049
Use term `bit twiddling`
timClicks Jul 8, 2025
818aecf
Merge branch 'unsafe-module' of github.com:accelerant-dev/comprehensi…
timClicks Jul 8, 2025
a30d518
Grammar - remove spurious comma
timClicks Jul 8, 2025
b014924
Fix grammar
timClicks Jul 8, 2025
af340b0
Fix spelling
timClicks Jul 8, 2025
6c28cfe
Change emphasis to what unsafe enables
timClicks Jul 8, 2025
c4c7382
Add TODO marker
timClicks Jul 9, 2025
92af3d0
Finish draft of "less powrful than it seems"
timClicks Jul 9, 2025
d1b856c
Add deeplink to blog post
timClicks Jul 9, 2025
df1a936
Replace the word "misunderstood" with something clearer
timClicks Jul 9, 2025
16701fa
Clarify course description
timClicks Jul 14, 2025
594a73f
Add timing directives
timClicks Jul 14, 2025
7dc18c9
Expand setup instructions
timClicks Jul 14, 2025
8472b44
Minor: apply formatting
timClicks Jul 14, 2025
2fa2db4
Use timings in more yaml frontmatter
timClicks Jul 14, 2025
e967c50
Improve grammar
timClicks Jul 14, 2025
b5dfaf3
Use correct TOC macro
timClicks Jul 14, 2025
6f38614
Improve grammar
timClicks Jul 14, 2025
e3dc58e
Use 2024 raw pointer syntax
timClicks Jul 14, 2025
21bffd6
Merge branch 'unsafe-module' of github.com:accelerant-dev/comprehensi…
timClicks Jul 14, 2025
e3233c8
Finish sentence
timClicks Jul 14, 2025
2c63445
Prefer simpler language
timClicks Jul 14, 2025
22a2177
Add more detail
timClicks Jul 14, 2025
5565dfd
Expand interop section
timClicks Jul 14, 2025
b36bb4a
Finish sentence
timClicks Jul 14, 2025
06384ef
Merge branch 'main' into unsafe-module
timClicks Jul 14, 2025
746c565
Greatly expand interop chapter
timClicks Jul 15, 2025
3819887
Remove commentary about the local installation
timClicks Jul 15, 2025
34dd3eb
Initial sketch of mechanics segment
timClicks Jul 15, 2025
fdaf9cd
Init mechanics segment
timClicks Jul 17, 2025
9c070c9
Start of Mechanics segment
timClicks Jul 17, 2025
bf646f8
Expand unsafe deep dive
timClicks Jul 21, 2025
ebcff61
Merge branch 'google:main' into unsafe-deep-dive/mechanics
timClicks Jul 21, 2025
69154fd
Resolve merge conflicts
timClicks Jul 23, 2025
496b0c6
Finish Guidelines intro slide
timClicks Jul 23, 2025
9b316f6
Refactor file names for consistency
timClicks Jul 23, 2025
101a071
Continue merging stray conflicts
timClicks Jul 23, 2025
2580b8a
Remove 'only even numbers' example
timClicks Jul 23, 2025
a879818
Improve instructor notes
timClicks Jul 23, 2025
05a68ee
Expand Mechanics segment
timClicks Jul 23, 2025
3f5f5c7
WIP: major re-write in progress
timClicks Jul 25, 2025
04f0517
Merge branch 'google:main' into unsafe-deep-dive/mechanics
timClicks Jul 25, 2025
003ae72
WIP: continuing rewrite; checking in progress
timClicks Jul 28, 2025
4a71a62
Merge branch 'unsafe-deep-dive/mechanics' of github.com:accelerant-de…
timClicks Jul 28, 2025
ad96bac
WIP: bringing in rawvec case study notes
timClicks Jul 30, 2025
e406d0e
Updating intro to UB chapter
timClicks Jul 30, 2025
f153600
WIP: initialization chapter
timClicks Aug 7, 2025
7261123
WIP: starting a virtual mem intro
timClicks Aug 12, 2025
a0d526a
WIP: more content
timClicks Aug 13, 2025
95dbe84
More content on memory lifecycle
timClicks Aug 13, 2025
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
18 changes: 18 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,24 @@
- [Data structures are safe](unsafe-deep-dive/foundations/data-structures-are-safe.md)
- [Actions might not be](unsafe-deep-dive/foundations/actions-might-not-be.md)
- [Less powerful than it seems](unsafe-deep-dive/foundations/less-powerful.md)
- [Understanding Unsafety](unsafe-deep-dive/understanding-unsafety.md)
- [Undefined behavior](unsafe-deep-dive/understanding-unsafety/undefined-behavior.md)
- [Out of bounds](unsafe-deep-dive/understanding-unsafety/out-of-bounds.md)
- [Initialization](unsafe-deep-dive/understanding-unsafety/initialization.md)
- [Mechanics](unsafe-deep-dive/mechanics.md)
- [Example: Representing Booleans](unsafe-deep-dive/mechanics/representing-booleans.md)
- [Extension: Representing Char](unsafe-deep-dive/mechanics/representing-char.md)
- [Example: FFI](unsafe-deep-dive/mechanics/example-ffi.md)
- [Case Study: RawVec](unsafe-deep-dive/mechanics/case-study-rawvec.md)
- [Case Study: std::mem](unsafe-deep-dive/mechanics/case-study-std-mem.md)
- [Case Study: UnsafeCell](unsafe-deep-dive/mechanics/case-study-unsafe-cell.md)
- [Guidelines](unsafe-deep-dive/mechanics/guidelines.md)
- [Portal types](unsafe-deep-dive/mechanics/guideline-portal-types.md)
- [Smart constructors](unsafe-deep-dive/mechanics/guideline-smart-constructors.md)
- [Reuse pre-existing code](unsafe-deep-dive/mechanics/guideline-reuse-preexisting.md)
- [Narrow scope](unsafe-deep-dive/mechanics/guideline-narrow-scope.md)
- [Safety comments](unsafe-deep-dive/mechanics/guideline-safety-comments.md)
- [Invariant checklist](unsafe-deep-dive/mechanics/guideline-invariant-checklist.md)

---

Expand Down
6 changes: 3 additions & 3 deletions src/running-the-course/course-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ You should be familiar with the material in
### Unsafe (Work in Progress)

The [Unsafe](../unsafe-deep-dive/welcome.md) deep dive is a two-day class on the
_unsafe_ Rust language. It covers the fundamentals of Rust's safety guarantees,
the motivation for `unsafe`, review process for `unsafe` code, FFI basics, and
building data structures that the borrow checker would normally reject.
_unsafe_ Rust language. It covers the fundamentals of what Rust's safety
guarantees are and why `unsafe` is needed, reviewing `unsafe` code, using FFI,
and building data structures that the borrow checker would normally reject.

{{%course outline Unsafe}}

Expand Down
13 changes: 13 additions & 0 deletions src/unsafe-deep-dive/mechanics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Mechanics

We've seen a number of unsafe blocks. We'll now discuss what to look for in
well-written unsafe code.

{{% segment outline}}

<details>

Inform the class that we will be doing more coding and group work in this
segment, rather than simply reading slides.

</details>
62 changes: 62 additions & 0 deletions src/unsafe-deep-dive/mechanics/case-study-rawvec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
minutes: 15
---

# Case Study: RawVec

> WORK IN PROGRESS
>
> This section is likely to receive significant alterations before completion
> and may even be removed entirely.

Many important collections in the standard library, such as `Vec<T>`, `String`
and `Deque<T>` rely on a private inner type called `RawVec<T>`.

Why is that inner type used?

```rust,ignore
// https://doc.rust-lang.org/src/alloc/vec/mod.rs.html
// std::alloc
pub struct Vec<T, A: Allocator = Global> {
buf: RawVec<T, A>,
len: usize,
}
```

```rust,ignore
// std::raw_vec
pub(crate) struct RawVec<T, A: Allocator = Global> {
inner: RawVecInner<A>,
_marker: PhantomData<T>,
}

struct RawVecInner<A: Allocator = Global> {
ptr: Unique<u8>,
/// Never used for ZSTs; it's `capacity()`'s responsibility to return usize::MAX in that case.
///
/// # Safety
///s
/// `cap` must be in the `0..=isize::MAX` range.
cap: Cap,
alloc: A,
}
```

The [implementation of `RawVec` is described in the Rustonomicon][rv].

[rv]: https://doc.rust-lang.org/nomicon/vec/vec-raw.html

<details>

`Vec<T>` is normally described as being a struct with three fields: length,
capacity, and pointer to an underlying buffer. Once you dig into the
implementation details, you'll notice that things are much more complicated.

`RawVec<T>` provides a barrier between Safe and Unsafe.

`RawVec<T>`

`RawVecInner<A>` contains the actual pointer and capacity of the underlying
buffer.

</details>
1 change: 1 addition & 0 deletions src/unsafe-deep-dive/mechanics/case-study-std-mem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Case Study: std::mem
1 change: 1 addition & 0 deletions src/unsafe-deep-dive/mechanics/case-study-unsafe-cell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Case Study: UnsafeCell
25 changes: 25 additions & 0 deletions src/unsafe-deep-dive/mechanics/case-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Case Study: Lesser-known parts of std::mem

As a group, we'll study some parts of Rust's memory management functionality:

- `std::mem::TransmuteFrom` trait and its `Assume` struct
- `std::mem::discriminant`
- `std::mem::forget_unsized`
- `std::mem::MaybeUninit<T>`

<details>

Split learners into small groups and ask them to look into the implementation of
one of the types above.

You may need to show learner how to view the source code of the standard
library.

After a few minutes, they should be able to answer the following questions:

- What is the purpose of the function/type/trait?
- How does the documentation describe the safety contract, if any? Can that
documentation be improved?
- Were there any interesting parts in its implementation?

</details>
1 change: 1 addition & 0 deletions src/unsafe-deep-dive/mechanics/example-ffi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Example: FFI
52 changes: 52 additions & 0 deletions src/unsafe-deep-dive/mechanics/guideline-invariant-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Safety checklist

When writing and reviewing `unsafe` code, we should make sure that we've
considered the following considerations _and documented_ what callers must later
uphold:

- Validity
- Alignment
- Lifetimes
- Ownership
- Platform
- Compliance with specifications

<details>

**Validity**

Callers must ensure that values must match some bit-pattern.

**Alignment**

Callers must ensure that values are correctly aligned.

**Lifetimes**

Do callers need to verify that a referent must exist before/after/during?

**Ownership**

Can this function generate confusion about ownership?

> _Aside:_ Memory leaks
>
> A discussion about leaking memory may arise here. If calling a function
> removes all ownership information, then .
>
> Memory leaking is not strictly a memory safety concern. However, it's often a
> problem in practice, especially if it is unintentional.
>
> Therefore, this should at least be documented. If it's possible to mishandle
> the API and cause an unintentional leak, then there is a case for an unsafe
> block.

**Platform**

Callers must be wary of platform-specific behavior.

**Compliance with specifications**

"Business Rules", i.e. all values must be even numbers.

</details>
49 changes: 49 additions & 0 deletions src/unsafe-deep-dive/mechanics/guideline-narrow-scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
minutes: 3
---

# Keep unsafe narrow

Compare these two code examples:

```rust
fn main() {
let raw = b"Crab";

// SAFETY: `raw` has the static lifetime of valid UTF-8 data and therefore `ptr` is valid
let crab = unsafe {
let ptr = raw.as_ptr();
let bytes = std::slice::from_raw_parts(ptr, 4);
std::str::from_utf8_unchecked(bytes)
};

println!("{crab}");
}
```

```rust
fn main() {
let raw = b"Crab";
let ptr = raw.as_ptr();

// SAFETY: `raw` has the static lifetime and therefore `ptr` is valid
let bytes = unsafe { std::slice::from_raw_parts(ptr, 4) };

// SAFETY: We created `raw` with valid UTF-8 data
let crab = unsafe { std::str::from_utf8_unchecked(bytes) };

println!("{crab}");
}
```

<details>

Unsafe blocks should have a narrow lens.

<!-- TODO(timclicks): fix clunky wording below -->

If an unsafe block has multiple safety conditions that can be assessed
independently, then it's likely that each of those conditions should be in its
own block.

</details>
16 changes: 16 additions & 0 deletions src/unsafe-deep-dive/mechanics/guideline-portal-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
minutes: 2
---

# Portal types

> TODO(timclicks): expand

Create a safe type that wraps a type that performs unsafe operations. The safe
type makes the unsafe type impossible to misuse. The wrapper acts as a portal to
the world of unsafe.

Examples:

- `std::collections::Vec` wraps `std::alloc::RawVec`
- The "sys crate" pattern
23 changes: 23 additions & 0 deletions src/unsafe-deep-dive/mechanics/guideline-reuse-preexisting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
minutes: 3
---

# Reuse pre-existing code

> TODO(timclicks): expand

Avoid re-implementing:

- Interior mutability &ndash; `Cell` and `UnsafeCell`
- Wrapping NULL pointers safely &ndash; `Option<&mut T>`

<details>

When we are writing code, it can be tempting to write everything from scratch.
Check whether pre-existing solutions exist already. In particular, the standard
library offers excellent defaults for memory management.

If you find yourself writing a better implementation of these types, then
consider submitting them to the Rust project.

</details>
67 changes: 67 additions & 0 deletions src/unsafe-deep-dive/mechanics/guideline-safety-comments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
minutes: 2
---

# Safety comments

When defining unsafe functions, provide a `Safety` section in the docstring:

```rust,editable
/// Compress `data`, overwriting its memory and updating the length of the slice.
unsafe fn compress_inplace(data: &mut [u8]) {
todo!();
}
```

When using an unsafe block, document how you have upheld your side of the
contract:

```rust,editable
unsafe {
std::mem::transmute::<i32>(x)
}
```

<details>

## Code

```rust
/// Compress `data`, overwriting its memory and updating the length of the slice.
///
/// ## Safety
///
/// Callers must ensure that the data's compressed form is shorter than the
/// original. As a heuristic, this function should not be used on a buffer
/// that has fewer than 256 bytes.
unsafe fn compress_inplace(data: &mut [u8]) {
todo!();
}
```

```rust
/// SAFETY: We control the generation of `x` and can ensure that it's 4 bytes wide
unsafe {
std::mem::transmute::<i32>(x)
}
```

> _Aside: In-place compression_
>
> Creating an algorithm that does in-place compression is likely to nerd snipe 1
> or two people. Avoid getting distracted.
>
> You could mention that it's possible to use a stack-allocated tmp buffer
> rather than something on the heap. If the implementation uses a static buffer,
> the comment must be updated to mention that the code is not thread-safe.

## Discussion

An effective safety comment is falsifiable. That is, there should be something
empirical that people can point to and check.

Note that Clippy's lint for safety comments does little more than check that the
string SAFETY: appears before the `unsafe` keyword. There is no further
validation.

</details>
Loading
Loading