1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2026-03-15 00:04:48 +02:00

docs: fix typos and grammar in unsafe deep dive pages (#3022)

Fix grammar, spelling, and capitalization across the Unsafe Rust deep
dive chapters. Ran `dprint fmt`.

---------

Co-authored-by: Martin Geisler <martin@geisler.net>
This commit is contained in:
Dmitri Gribenko
2026-01-12 01:18:28 +01:00
committed by GitHub
parent d0eee96697
commit b17d243ced
37 changed files with 103 additions and 98 deletions

View File

@@ -2,8 +2,8 @@
Uses of `unsafe` in production that may be useful for further study:
| Case study | Topics |
| ------------------------------- | -------------------------------------- |
| tokio's [Intrusive Linked List] | UnsafeCell, PhantomData, PhantomPinned |
| Case study | Topics |
| --------------------------------- | -------------------------------------- |
| `tokio`'s [Intrusive Linked List] | UnsafeCell, PhantomData, PhantomPinned |
[Intrusive Linked List]: case-studies/intrusive-linked-list.md

View File

@@ -1,7 +1,7 @@
This segment of the class is about the foreign function interface with Rust.
outline:
Outline:
Start by wrapping a simple C function
progress into more complex cases which involve pointers and uninitialized memory
- Start by wrapping a simple C function.
- Progress into more complex cases which involve pointers and uninitialized
memory.

View File

@@ -32,9 +32,9 @@ unsafe extern "C" {
}
```
Explain that many POSIX functions are available Rust because cargo links against
the C standard library (libc) by default, which makes its symbols into the
program’s scope.
Explain that many POSIX functions are available in Rust because Cargo links
against the C standard library (libc) by default, which brings its symbols into
the program’s scope.
Show `man 3 abs` in the terminal or [a webpage][abs].
@@ -53,7 +53,7 @@ unsafe extern "C" {
Discuss rationale: using `ffi::c_int` increases the portability of our code.
When the standard library is compiled for the target platform, the platform can
determine the widths. According to the C standard, an `c_int` may be defined as
determine the widths. According to the C standard, a `c_int` may be defined as
an `i16` rather than the much more common `i32`.
(Optional) Show the [documentation for c_int][c_int] to reveal that it is a type
@@ -87,7 +87,8 @@ unsafe extern "C" {
}
```
Explain the `safe fn` marks `abs` as safe to call without an `unsafe` block.
Explain that the `safe fn` marks `abs` as safe to call without an `unsafe`
block.
Completed program for reference:

View File

@@ -17,7 +17,7 @@ class StringInterner {
std::unordered_set<std::string> strings;
public:
// Returns pointer to interned string (valid for lifetime of interner)
// Returns a pointer to the interned string (valid for lifetime of interner)
const char* intern(const char* s) {
auto [it, _] = strings.emplace(s);
return it->c_str();

View File

@@ -14,12 +14,13 @@ minutes: 3
<details>
C++ includes a number of features that dont exist in C with an FFI impact:
C++ includes a number of features that don't exist in C with an FFI impact:
Overloading: overloads become impossible to express because of name mangling
Overloading: Overloads become impossible to express because of name mangling
Exceptions: must catch exceptions at the FFI boundary and convert as escaping
exceptions in `extern "C"` functions is undefined behavior
Exceptions: Must catch exceptions at the FFI boundary and convert them to error
codes, as escaping exceptions in `extern "C"` functions constitute undefined
behavior
Destructors: C callers won't run destructors; must expose explicit `*_destroy()`
functions

View File

@@ -19,17 +19,17 @@ Errors: Must convert `Result` to abide by C conventions; easy to forget to check
errors on C side.
Strings: Conversion cost; null bytes in Rust strings cause truncation; UTF-8
validation on ingress
validation on ingress.
Nullability: Every pointer from C must be checked to create an
`Option<NonNull<T>>`, implying unsafe blocks or runtime cost
`Option<NonNull<T>>`, implying unsafe blocks or runtime cost.
Ownership: Must document and enforce object lifetimes manually
Ownership: Must document and enforce object lifetimes manually.
Callbacks: Must decompose closures into fn pointer + context; lifetime of
context is manual
context is manual.
Panics: Panic across FFI boundary is undefined behavior; must catch at boundary
with `catch_unwind`
with `catch_unwind`.
</details>

View File

@@ -18,7 +18,7 @@ the languages that impact FFI:
_Trivial relocatability_
Cannot safely move C++ objects on Rust side; must pin or keep in C++ heap.
Cannot safely move C++ objects on the Rust side; must pin or keep in C++ heap.
In Rust, object movement, which occurs during assignment or by being passed by
value, always copies values bit by bit.
@@ -35,10 +35,10 @@ Objects with the same semantics are impossible to define in Rust.
_Destruction safety_
Moved-from C++ object semantics don't map; must prevent Rust from "moving" C++
types
types.
_Exception safety_
Neither can cross into the other safely; both must catch at boundary
Neither can cross into the other safely; both must catch at the boundary.
</details>

View File

@@ -14,13 +14,13 @@ Steps:
use std::mem::MaybeUninit;
fn main() {
// Step 1: create MaybeUninit
// Step 1: Create MaybeUninit
let mut uninit = MaybeUninit::uninit();
// Step 2: write a valid value to the memory
// Step 2: Write a valid value to the memory
uninit.write(1);
// Step 3: inform the type system that the memory location is valid
// Step 3: Inform the type system that the memory location is valid
let init = unsafe { uninit.assume_init() };
println!("{init}");
@@ -29,16 +29,17 @@ fn main() {
<details>
To work with uninitialized memory, follow this general workflow: create, write,
confirm.
To work with uninitialized memory, follow this general workflow: create, write,
confirm.
1. Create `MaybeUninit<T>`. The `::uninit()` constructor is the most general
purpose one, but there are others which perform a write as well.
1. Create `MaybeUninit<T>`. The `::uninit()` constructor is the most
general-purpose one, but there are others which perform a write as well.
2. Write a value of T. Notice that this is available from safe Rust. Staying in
safe Rust is useful because you must ensure that the value you write is valid.”
2. Write a value of T. Notice that this is available from safe Rust. Staying in
safe Rust is useful because you must ensure that the value you write is
valid.
3. Confirm to the type system that the memory is now initialized with the
`.assume_init()` method.
3. Confirm to the type system that the memory is now initialized with the
`.assume_init()` method.
</details>

View File

@@ -53,9 +53,9 @@ When creating a sub-slice of a partially-initialized array, be careful with
ownership and correctly implementing drop. Reminder: `MaybeUninit<T>` will not
call drop on its `T`.
`MaybeUninit<[u8;2048]>` is distinct from `[MaybeUninit::<u8>; 2048]` the
difference between an array of uninitialized memory and an array that contains
uninitialized elements.
`MaybeUninit<[u8;2048]>` is distinct from `[MaybeUninit::<u8>; 2048]`. This is
the difference between an array of uninitialized memory and an array that
contains uninitialized elements.
- `MaybeUninit<[u8;2048]>` is "all or nothing". You either fully initialize the
whole array and then call `assume_init`, or you must keep it as

View File

@@ -24,7 +24,7 @@ Q: “Although the memory has been written to, the type remains `MaybeUninit<T>`
Can anyone think of why?”
A: Some types require their values to be non-zero or non-null. The classic case
is references, but this applies to many other types as well. Consider
is references, but this applies to many other types as well. Consider the
`NonZeroUsize` integer type and others in its family.
</details>

View File

@@ -34,8 +34,8 @@ know how many bytes you'll receive. Using `MaybeUninit<T>` lets you allocate the
buffer once without paying for a redundant initialization pass.
If we were to create the array with the standard syntax (`buf = [0u8; 2048]`),
the whole buffer will be flushed with zeroes. `MaybeUninit<T>` tells the
compiler to reserve space, but don't touch the memory yet.
the whole buffer would be flushed with zeroes. `MaybeUninit<T>` tells the
compiler to reserve space, but not to touch the memory yet.
Q: Which part of the code snippet is performing a similar role to
`.assume_init()`? A: The pointer cast and the implicit read.

View File

@@ -37,9 +37,9 @@ fn main() {
Code using `unsafe` _might_ be faster.
`fast_sum()` skips skips bounds checks. However, benchmarking is necessary to
validate performance claims. For cases like this, Rust's iterators can usually
elide bounds checks anyway.
`fast_sum()` skips bounds checks. However, benchmarking is necessary to validate
performance claims. For cases like this, Rust's iterators can usually elide
bounds checks anyway.
Optional: [show identical generated assembly][godbolt] for the two functions.

View File

@@ -50,7 +50,8 @@ The unsafe operations from the [Rust Reference] (Avoid spending too much time):
> - Accessing a field of a union, other than to assign to it.
> - Calling an `unsafe` function.
> - Calling a safe function marked with a `<target_feature>` from a function
> that does not have a `<target_feature>` attribute enabling the same features
> that does not have a `<target_feature>` attribute enabling the same
> features.
> - Implementing an unsafe trait.
> - Declaring an extern block.
> - Applying an unsafe attribute to an item.

View File

@@ -17,7 +17,7 @@ Enhanced code review
<details>
“The unsafe keyword places more responsibility on the programmer, therefore it
“The unsafe keyword places more responsibility on the programmer; therefore it
requires a stronger development workflow.
“This class assumes a specific software development workflow where code review

View File

@@ -24,7 +24,7 @@ the compiler to programmers. It signals that there are preconditions that must
be satisfied.
“To uphold that responsibility, programmers must ensure that they've understood
what the preconditions are and that they code will always satisfy them.
what the preconditions are and that their code will always satisfy them.
“Throughout this course, we'll use the term _safety preconditions_ to describe
this situation.”

View File

@@ -34,7 +34,7 @@ defining unsafe functions and unsafe traits.
careful.”
“The creator of the API should communicate what care needs to be taken. Unsafe
APIs are not complete without documentation about safety requirements.. Callers
APIs are not complete without documentation about safety requirements. Callers
need to know that they have satisfied any requirements, and that’s impossible if
they’re not written down.”

View File

@@ -1,4 +1,4 @@
# Warm up examples
# Warm-up examples
Examples to demonstrate:

View File

@@ -1,7 +1,7 @@
# Memory Lifecycle
Memory moves through different phases as objects (values) are created and
destroyed
destroyed.
| Memory State | Readable from Safe Rust? |
| ------------ | ------------------------ |
@@ -11,8 +11,8 @@ destroyed
<details>
This section discusses what it happens as memory from the operating system
becomes a valid variable in the program.
This section discusses what happens as memory from the operating system becomes
a valid variable in the program.
When memory is available, the operating system has provided our program with it.

View File

@@ -2,14 +2,14 @@
minutes: 1
---
# Why for learning unsafe Rust
# Why learn unsafe Rust?
We know that writing code without the guarantees that Rust provides ...
> “Use-after-free (UAF), integer overflows, and out of bounds (OOB) reads/writes
> comprise 90% of vulnerabilities with OOB being the most common.”
>
> --— **Jeff Vander Stoep and Chong Zang**, Google.
> — **Jeff Vander Stoep and Chong Zhang**, Google.
> "[Queue the Hardening Enhancements](https://security.googleblog.com/2019/05/queue-hardening-enhancements.html)"
... so why is `unsafe` part of the language?

View File

@@ -1,8 +1,8 @@
# pinning
# Pinning
> **Important Note**
>
> To not add this section to the project's SUMMARY.md yet. Once CLs/PRs to
> Do not add this section to the project's SUMMARY.md yet. Once CLs/PRs to
> accept all the new segments for the Unsafe Deep Dive have been included in the
> repository, an update to SUMMARY.md will be made.

View File

@@ -26,9 +26,9 @@ that implements `Deref`.
However, `Pin::new()` only accepts types that dereference into a target that
implements `Unpin` (`Deref<Target: Unpin>`). This allows `Pin` to rely on the
the type system to enforce its guarantees.
type system to enforce its guarantees.
Types that do not implement `Unpin`, i.e. types that require pinning, must
Types that do not implement `Unpin`, i.e., types that require pinning, must
create a `Pin` via the unsafe `Pin::new_unchecked()`.
Aside: Unlike other `new()`/`new_unchecked()` method pairs, `new` does not do

View File

@@ -2,7 +2,7 @@
minutes: 5
---
# Worked example Implementing `Drop` for `!Unpin` types
# Worked Example: Implementing `Drop` for `!Unpin` types
```rust,editable,ignore
use std::cell::RefCell;
@@ -67,7 +67,7 @@ telemetry or logging.
`self.data` in an invalid state. `self.data` will be dropped again at the end of
the method, which is a double free.
Ask the class for fix the code.
Ask the class to fix the code.
**Suggestion 0: Redesign**

View File

@@ -2,10 +2,10 @@
minutes: 10
---
# With Pin&lt;Ptr&gt;
# With `Pin<Ptr>`
Pinning allows Rust programmers to create a type which is much more similar to
the C++ class.
C++ classes.
```rust,editable
use std::marker::PhantomPinned;

View File

@@ -63,7 +63,7 @@ Talking points:
- Emphasize that `unsafe` appears frequently. This is a hint that another design
may be more appropriate.
- `unsafe` blocks lack of safety comments. Therefore, this code is unsound.
- `unsafe` blocks lack safety comments. Therefore, this code is unsound.
- `unsafe` blocks are too broad. Good practice uses smaller `unsafe` blocks with
specific behavior, specific preconditions and specific safety comments.

View File

@@ -31,7 +31,7 @@ pub struct SelfReferentialBuffer {
class SelfReferentialBuffer {
char data[1024];
char* cursor;
}
};
```
<details>

View File

@@ -3,7 +3,7 @@
<details>
“We've seen many examples of code that has problems in the class, but we lack
consistent terminology
consistent terminology.
“The goal of the next section is to introduce some terms that describe many of
the concepts that we have been thinking about.

View File

@@ -2,7 +2,7 @@
minutes: 5
---
# 3 shapes of sound Rust
# 3 Shapes of Sound Rust
- Functions written only in Safe Rust
- Functions that contain `unsafe` blocks which are impossible to misuse

View File

@@ -23,7 +23,7 @@ fn main() {
<details>
“The implementation only uses safe Rust
“The implementation only uses safe Rust.
What can we learn from this?

View File

@@ -18,7 +18,7 @@ problems.
“Sound code is made up of _sound functions_ and _sound operations_.
“A sound function as a function where none of its possible inputs could provoke
“A sound function is a function where none of its possible inputs could provoke
soundness problems.
Sound functions have common shapes.

View File

@@ -12,7 +12,8 @@ satisfied.
- Read the definition of sound functions.
- Remind the students that the programmer who implements the caller is
responsible to satisfy the safety precondition, the compiler is not helping.
responsible for satisfying the safety precondition; the compiler is not
helping.
- Translate into informal terms. Soundness means that the function is nice and
plays by the rules. It documents its safety preconditions, and when the caller

View File

@@ -16,12 +16,12 @@ Unsound code is _bad_.
- Read the definition of unsound functions.
- Translate into infomal terms: unsound code is not nice. No, that's an
- Translate into informal terms: unsound code is not nice. No, that's an
understatement. Unsound code is BAD. Even if you play by the documented rules,
unsound code can still trigger UB!
- We don't want any unsound code in our repositories.
- Finding unsound code is the **primary** goal of the code review
- Finding unsound code is the **primary** goal of the code review.
</details>

View File

@@ -29,7 +29,7 @@ impl<'a> Ascii<'a> {
"The `Ascii` type is a minimal wrapper around a byte slice. Internally, they
share the same representation. However, `Ascii` requires that the high bit must
is not be used."
not be used."
Optional: Extend the example to mention that it's possible to use
`debug_assert!` to test the preconditions during tests without impacting release

View File

@@ -38,7 +38,7 @@ Some conditions are even more subtle than they first seem.
Consider "in-bounds array access". Reading from the memory location, i.e.
dereferencing, is not required to break the program. Creating an out-of-bounds
reference already break's the compiler's assumptions, leading to erratic
reference already breaks the compiler's assumptions, leading to erratic
behavior.
Rust tells LLVM to use its `getelementptr inbounds` assumption. That assumption

View File

@@ -37,8 +37,8 @@ Highlight Safety section.
Click the "convertible to a reference" hyperlink to the "Pointer to reference
conversion"
Track down the rules for converting a pointer to a reference, aka is
"_deferencerable_".
Track down the rules for converting a pointer to a reference, i.e., whether it
is "dereferenceable".
Consider the implications of this excerpt (Rust 1.90.0) "You must enforce Rust’s
aliasing rules. The exact aliasing rules are not decided yet, ..."

View File

@@ -2,7 +2,7 @@
minutes: 10
---
# Example: references
# Example: References
```rust,editable,ignore
fn main() {
@@ -24,7 +24,7 @@ Confirm understanding of the syntax
enforced without assistance from the compiler.
- Note: raw pointers do not provide ownership info to Rust. A pointer can be
semantically owning the data, or semantically borrowing, but that
information only exists in the programmer's mind
information only exists in the programmer's mind.
- `&mut *boxed as *mut _` expression:
- `*boxed` is ...
@@ -36,30 +36,30 @@ Confirm understanding of the syntax
Confirm understanding of ownership
- Step through code
- (Line 3) Creates raw pointer to the `123` by de-referencing the box,
creating a new reference and casting the new reference as a pointer
- Step through code:
- (Line 3) Creates raw pointer to the `123` by dereferencing the box, creating
a new reference and casting the new reference as a pointer.
- (Line 4) Creates raw pointer with a NULL value
- (Line 7) Converts the raw pointer to an Option with `.as_mut()`
- Highlight that pointers are nullable in Rust (unlike references).
- Compile to reveal the error messages
- Compile to reveal the error messages.
- Discuss
- (Line 6) `println!("{:?}", *a);`
- Prefix star dereferences a raw pointer
- Prefix star dereferences a raw pointer.
- It is an explicit operation. Whereas regular references have implicit
dereferencing most of the time thanks to the Deref trait. This is referred
to as "auto-deref".
- Dereferencing a raw pointer is an unsafe operation
- Requires an unsafe block
- Dereferencing a raw pointer is an unsafe operation.
- Requires an unsafe block.
- (Line 7) `println!("{:?}", b.as_mut());`
- `as_mut()` is an unsafe function.
- Calling unsafe function requires an unsafe block
- Calling an unsafe function requires an unsafe block.
- Demonstrate: Fix the code (add unsafe blocks) and compile again to show the
working program
working program.
- Demonstrate: Replace `as *mut i32` with `as *mut _`, show that it compiles.
@@ -67,7 +67,7 @@ Confirm understanding of ownership
that the source of the cast is a `&mut i32`. This reference type can only be
converted to one pointer type, `*mut i32`.
- Add safety comments
- Add safety comments:
- We said that the unsafe code marks the responsibility shift from the
compiler to the programmer.
- How do we convey that we thought about our unusual responsibilities while
@@ -75,9 +75,9 @@ Confirm understanding of ownership
- Safety comments explain why unsafe code is correct.
- Without a safety comment, unsafe code is not safe.
- Discuss: Whether to use one large unsafe block or two smaller ones
- Possibility of using a single unsafe block rather than multiple
- Using more allows safety comments as specific as possible
- Discuss: Whether to use one large unsafe block or two smaller ones:
- Possibility of using a single unsafe block rather than multiple.
- Using more allows safety comments as specific as possible.
[ptr-as_mut]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut

View File

@@ -20,7 +20,7 @@ rustc 1.87
TODO (tim): We should be able to avoid this by just relying on the `cc` crate
We recommend that you install the [Bazel build system](https://bazel.build/install).
This will allow you to easily compile project that combine multiple languages.
This will allow you to easily compile projects that combine multiple languages.
-->
@@ -39,7 +39,7 @@ $ cargo serve # then open http://127.0.0.1:3000/ in a browser
<details>
Ask everyone to confirm that everyone is able to execute `rustc` with a version
older that 1.87.
newer than 1.87.
For those people who do not, tell them that we'll resolve that in the break.

View File

@@ -29,7 +29,7 @@ We’ll work on three areas:
The goal of this class is to teach you enough Unsafe Rust for you to be able to
review easy cases yourself, and distinguish difficult cases that need to be
reviewed my more experienced Unsafe Rust engineers.
reviewed by more experienced Unsafe Rust engineers.
- Establishing a mental model of Unsafe Rust
- what the `unsafe` keyword means
@@ -38,12 +38,12 @@ reviewed my more experienced Unsafe Rust engineers.
- common patterns
- expectations for code that uses `unsafe`
- Practice working with unsafe
- Practicing working with unsafe
- reading and writing both code and documentation
- use unsafe APIs
- design and implement them
- using unsafe APIs
- designing and implementing them
- Review code
- Reviewing code
- the confidence to self-review easy cases
- the knowledge to detect difficult cases