From dd7f7e1c1079745ac2d6bfeb8a5f2a63f83e2eff Mon Sep 17 00:00:00 2001 From: yudhi Date: Mon, 27 Jul 2026 18:17:49 +0700 Subject: [PATCH] unsafe-deep-dive: replace may_overflow with a real safety precondition The "Example: may_overflow" slide taught that signed integer overflow is undefined behavior in release mode and that the function therefore needs the `unsafe` keyword. Both claims are false: integer overflow is well defined in Rust (debug builds panic, release builds wrap), and `a + i32::MAX` does not require `unsafe` at all. Replace the example with an `unsafe fn element_at` built on `slice::get_unchecked`, which has a genuine safety precondition: the caller must keep the index in bounds. This preserves the slide's purpose (a concrete example of responsibility shifting to the programmer) and leads directly into the "Safety Preconditions" segment. The speaker notes state plainly that integer overflow is defined behavior and never requires `unsafe`. Closes #3122. --- src/SUMMARY.md | 2 +- .../introduction/may_overflow.md | 62 +++++++++---------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 34f9edc9549d..24045f9d326e 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -572,7 +572,7 @@ SPDX-License-Identifier: CC-BY-4.0 - [Sometimes useful](unsafe-deep-dive/introduction/characteristics-of-unsafe-rust/sometimes-useful.md) - [Responsibility shift](unsafe-deep-dive/introduction/responsibility-shift.md) - [Stronger development workflow required](unsafe-deep-dive/introduction/impact-on-workflow.md) - - [Example: may_overflow](unsafe-deep-dive/introduction/may_overflow.md) + - [Example: a safety precondition](unsafe-deep-dive/introduction/may_overflow.md) - [Safety Preconditions](unsafe-deep-dive/safety-preconditions.md) - [Common Preconditions](unsafe-deep-dive/safety-preconditions/common-preconditions.md) - [Getter example](unsafe-deep-dive/safety-preconditions/getter.md) diff --git a/src/unsafe-deep-dive/introduction/may_overflow.md b/src/unsafe-deep-dive/introduction/may_overflow.md index 678b34cd9cee..7999645a429b 100644 --- a/src/unsafe-deep-dive/introduction/may_overflow.md +++ b/src/unsafe-deep-dive/introduction/may_overflow.md @@ -7,53 +7,51 @@ Copyright 2026 Google LLC SPDX-License-Identifier: CC-BY-4.0 --> -# Example: may_overflow function +# Example: a safety precondition -```rust,should_panic,editable +```rust,editable # // Copyright 2026 Google LLC # // SPDX-License-Identifier: Apache-2.0 # -/// Adds 2^31 - 1 to negative numbers. -unsafe fn may_overflow(a: i32) -> i32 { - a + i32::MAX +/// Returns the element at `index` without bounds checking. +/// +/// # Safety +/// +/// The caller must guarantee that `index < slice.len()`. +unsafe fn element_at(slice: &[i32], index: usize) -> i32 { + unsafe { *slice.get_unchecked(index) } } fn main() { - let x = unsafe { may_overflow(123) }; - println!("{x}"); + let numbers = [10, 20, 30]; + // Safety: `1` is a valid index into `numbers`. + let value = unsafe { element_at(&numbers, 1) }; + println!("{value}"); } ```
-“The `unsafe` keyword may have a subtly different meaning than what some people -assume.” +“`slice.get_unchecked(index)` returns the element at `index` without the bounds +check that `slice[index]` performs. It is faster, but it is only sound when +`index < slice.len()`.” -“The code author believes that the code is correct. In principle, the code is -safe.” +“The compiler cannot verify that every caller respects this rule, so +`element_at` is declared `unsafe` and states the rule in its `# Safety` section. +Callers acknowledge that responsibility by wrapping the call in an `unsafe` +block.” -“In this toy example, the `may_overflow` function is only intended to be called -with negative numbers. +“Inside `element_at`, the call to `get_unchecked` is wrapped in its own `unsafe` +block, because the body of an `unsafe fn` is a safe context — the inner block is +what marks the unsafe operation.” -Ask learners if they can explain why `may_overflow` requires the unsafe keyword. +Ask the learners what happens if a caller passes an out-of-bounds index. The +skipped check makes the access undefined behavior, so the safety precondition +would be violated. The `unsafe` keyword is what shifts responsibility for +upholding the precondition from the compiler to the programmer. -“In case you’re unsure what the problem is, let’s pause briefly to explain. An -`i32` only has 31 bits available for positive numbers. When an operation -produces a result that requires more than 31 bits, then the program is put into -an invalid state. And it’s not just a numerical problem. Compilers optimize code -on the basis that invalid states are impossible. This causes code paths to be -deleted, producing erratic runtime behavior while also introducing security -vulnerabilities. - -Compile and run the code, producing a panic. Then run the example in the -playground to run under `--release` mode to trigger UB. - -“This code can be used correctly, however, improper usage is highly dangerous.” - -“And it's impossible for the compiler to verify that the usage is correct.” - -This is what we mean when we say that the `unsafe` keyword marks the location -where responsibility for memory safety shifts from the compiler to the -programmer. +“Note that integer overflow does not need this treatment: in Rust it is well +defined (debug builds panic, release builds wrap), so it is never undefined +behavior and never requires `unsafe`.”