1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-07-16 19:14:20 +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
fn main() {
let mut s = String::from("careful!");
let mut x = 10;
let r1 = &raw mut s;
let r2 = r1 as *const String;
let p1: *mut i32 = &raw mut x;
let p2 = p1 as *const i32;
// SAFETY: r1 and r2 were obtained from references and so are guaranteed to
// be non-null and properly aligned, the objects underlying the references
// from which they were obtained are live throughout the whole unsafe
// block, and they are not accessed either through the references or
// concurrently through any other pointers.
// SAFETY: p1 and p2 were created by taking raw pointers to a local, so they
// are guaranteed to be non-null, aligned, and point into a single (stack-)
// allocated object.
//
// 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 {
println!("r1 is: {}", *r1);
*r1 = String::from("uhoh");
println!("r2 is: {}", *r2);
dbg!(*p1);
*p1 = 6;
// 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 };
drop(s);
println!("r3 is: {}", *r3);
let r: &i32 = unsafe { &*p1 };
dbg!(r);
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.
The "NOT SAFE" section gives an example of a common kind of UB bug: `*r1` has
the `'static` lifetime, so `r3` has type `&'static String`, and thus outlives
`s`. Creating a reference from a pointer requires _great care_.
The "UNSOUND" section gives an example of a common kind of UB bug: naïvely
taking a reference to the dereference of a raw pointer sidesteps the compiler's
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>

View File

@ -14,15 +14,18 @@ fn main() {
}
```
However, since data races can occur, it is unsafe to read and write mutable
static variables:
However, mutable static variables are unsafe to read and write because multiple
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
static mut COUNTER: u32 = 0;
fn add_to_counter(inc: u32) {
// SAFETY: There are no other threads which could be accessing `COUNTER`.
#[allow(static_mut_refs)]
unsafe {
COUNTER += inc;
}
@ -32,7 +35,6 @@ fn main() {
add_to_counter(42);
// SAFETY: There are no other threads which could be accessing `COUNTER`.
#[allow(static_mut_refs)]
unsafe {
dbg!(COUNTER);
}
@ -41,13 +43,12 @@ fn main() {
<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
removing the `unsafe` and see how the compiler explains that it is undefined
behavior to access a mutable static from multiple threads.
- Rust 2024 edition goes further and makes accessing a mutable static by
reference an error by default. We work around this in the example with
`#[allow(static_mut_refs)]`. Don't do this.
- The 2024 Rust edition goes further and makes accessing a mutable static by
reference an error by default.
- Using a mutable static is almost always a bad idea, you should use interior
mutability instead.
- 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
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`.
- Foreign functions in `extern "C"` blocks.
- Rust functions declared unsafe.
- Unsafe foreign functions in `extern "C"` blocks.
<details>

View File

@ -1,6 +1,9 @@
# 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
use std::ffi::c_char;