1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-07-17 11:27:57 +02:00

A few fixes for the unsafe chapter (#2805)

See individual commits in https://github.com/google/comprehensive-rust/pull/2805. Fixes #2734.
This commit is contained in:
Frances Wingerter
2025-07-05 17:46:42 +00:00
committed by GitHub
parent 7c23d3bcf8
commit 8419b3095e
4 changed files with 42 additions and 30 deletions

View File

@ -8,27 +8,32 @@ Creating pointers is safe, but dereferencing them requires `unsafe`:
```rust,editable ```rust,editable
fn main() { fn main() {
let mut s = String::from("careful!"); let mut x = 10;
let r1 = &raw mut s; let p1: *mut i32 = &raw mut x;
let r2 = r1 as *const String; let p2 = p1 as *const i32;
// SAFETY: r1 and r2 were obtained from references and so are guaranteed to // SAFETY: p1 and p2 were created by taking raw pointers to a local, so they
// be non-null and properly aligned, the objects underlying the references // are guaranteed to be non-null, aligned, and point into a single (stack-)
// from which they were obtained are live throughout the whole unsafe // allocated object.
// block, and they are not accessed either through the references or //
// concurrently through any other pointers. // The object underlying the raw pointers lives for the entire function, so
// it is not deallocated while the raw pointers still exist. It is not
// accessed through references while the raw pointers exist, nor is it
// accessed from other threads concurrently.
unsafe { unsafe {
println!("r1 is: {}", *r1); dbg!(*p1);
*r1 = String::from("uhoh"); *p1 = 6;
println!("r2 is: {}", *r2); // Mutation may soundly be observed through a raw pointer, like in C.
dbg!(*p2);
} }
// NOT SAFE. DO NOT DO THIS. // UNSOUND. DO NOT DO THIS.
/* /*
let r3: &String = unsafe { &*r1 }; let r: &i32 = unsafe { &*p1 };
drop(s); dbg!(r);
println!("r3 is: {}", *r3); x = 50;
dbg!(r); // Object underlying the reference has been mutated. This is UB.
*/ */
} }
``` ```
@ -52,8 +57,11 @@ In the case of pointer dereferences, this means that the pointers must be
In most cases the pointer must also be properly aligned. In most cases the pointer must also be properly aligned.
The "NOT SAFE" section gives an example of a common kind of UB bug: `*r1` has The "UNSOUND" section gives an example of a common kind of UB bug: naïvely
the `'static` lifetime, so `r3` has type `&'static String`, and thus outlives taking a reference to the dereference of a raw pointer sidesteps the compiler's
`s`. Creating a reference from a pointer requires _great care_. knowledge of what object the reference is actually pointing to. As such, the
borrow checker does not freeze `x` and so we are able to modify it despite the
existence of a reference to it. Creating a reference from a pointer requires
_great care_.
</details> </details>

View File

@ -14,15 +14,18 @@ fn main() {
} }
``` ```
However, since data races can occur, it is unsafe to read and write mutable However, mutable static variables are unsafe to read and write because multiple
static variables: threads could do so concurrently without synchronization, constituting a data
race.
Using mutable statics soundly requires reasoning about concurrency without the
compiler's help:
```rust,editable ```rust,editable
static mut COUNTER: u32 = 0; static mut COUNTER: u32 = 0;
fn add_to_counter(inc: u32) { fn add_to_counter(inc: u32) {
// SAFETY: There are no other threads which could be accessing `COUNTER`. // SAFETY: There are no other threads which could be accessing `COUNTER`.
#[allow(static_mut_refs)]
unsafe { unsafe {
COUNTER += inc; COUNTER += inc;
} }
@ -32,7 +35,6 @@ fn main() {
add_to_counter(42); add_to_counter(42);
// SAFETY: There are no other threads which could be accessing `COUNTER`. // SAFETY: There are no other threads which could be accessing `COUNTER`.
#[allow(static_mut_refs)]
unsafe { unsafe {
dbg!(COUNTER); dbg!(COUNTER);
} }
@ -41,13 +43,12 @@ fn main() {
<details> <details>
- The program here is safe because it is single-threaded. However, the Rust - The program here is sound because it is single-threaded. However, the Rust
compiler reasons about functions individually so can't assume that. Try compiler reasons about functions individually so can't assume that. Try
removing the `unsafe` and see how the compiler explains that it is undefined removing the `unsafe` and see how the compiler explains that it is undefined
behavior to access a mutable static from multiple threads. behavior to access a mutable static from multiple threads.
- Rust 2024 edition goes further and makes accessing a mutable static by - The 2024 Rust edition goes further and makes accessing a mutable static by
reference an error by default. We work around this in the example with reference an error by default.
`#[allow(static_mut_refs)]`. Don't do this.
- Using a mutable static is almost always a bad idea, you should use interior - Using a mutable static is almost always a bad idea, you should use interior
mutability instead. mutability instead.
- There are some cases where it might be necessary in low-level `no_std` code, - There are some cases where it might be necessary in low-level `no_std` code,

View File

@ -7,10 +7,10 @@ minutes: 15
A function or method can be marked `unsafe` if it has extra preconditions you A function or method can be marked `unsafe` if it has extra preconditions you
must uphold to avoid undefined behaviour. must uphold to avoid undefined behaviour.
There are two main categories: Unsafe functions may come from two places:
- Rust functions declared unsafe with `unsafe fn`. - Rust functions declared unsafe.
- Foreign functions in `extern "C"` blocks. - Unsafe foreign functions in `extern "C"` blocks.
<details> <details>

View File

@ -1,6 +1,9 @@
# Unsafe External Functions # Unsafe External Functions
Functions in a foreign language may also be unsafe: You can declare foreign functions for access from Rust with `unsafe extern`.
This is unsafe because the compiler has to way to reason about their behavior.
Functions declared in an `extern` block must be marked as `safe` or `unsafe`,
depending on whether they have preconditions for safe use:
```rust,editable ```rust,editable
use std::ffi::c_char; use std::ffi::c_char;