From 097b700bc3a3cedcd6576527377666d6a6a151d7 Mon Sep 17 00:00:00 2001 From: Nicole LeGare Date: Thu, 17 Apr 2025 16:48:01 -0700 Subject: [PATCH 1/4] Replace one of the examples of the aliasing rule --- src/borrowing/examples.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/borrowing/examples.md b/src/borrowing/examples.md index b90ba6471a70..cb3ad36b4fb7 100644 --- a/src/borrowing/examples.md +++ b/src/borrowing/examples.md @@ -17,21 +17,36 @@ fn main() { } ``` -Similarly, consider the case of iterator invalidation: +We can also look at a case where these rules prevent incorrect optimizations: ```rust,editable,compile_fail +fn sum_and_zero(a: &mut i32, b: &mut i32) { + *a = *a + *b; + *b = 0; +} + fn main() { - let mut vec = vec![1, 2, 3, 4, 5]; - for elem in &vec { - vec.push(elem * 2); - } + let mut x = 5; + sum_and_zero(&mut x, &mut x); } ```
-- In both of these cases, modifying the collection by pushing new elements into +- In the first case, modifying the collection by pushing new elements into it can potentially invalidate existing references to the collection's elements if the collection has to reallocate. +- In the second case, the aliasing rule prevents mis-compilation: The output of + `sum_and_zero` depends on the ordering of the two operations, which means if + the compiler swaps the order of these operations (which it's allowed to do) it + changes the result. + + - The equivalent code in C exhibits undefined behavior, which may result in + mis-compilation and unexpected behavior, even if it doesn't cause a crash. + + - Rust's aliasing rules provide strong guarantees about how references can + alias, allowing the compiler to apply optimizations without breaking the + semantics of your program. +
From 24a26427584fcc224cc63884f070dea09c882200 Mon Sep 17 00:00:00 2001 From: Nicole LeGare Date: Thu, 17 Apr 2025 16:53:30 -0700 Subject: [PATCH 2/4] Fix formatting --- src/borrowing/examples.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/borrowing/examples.md b/src/borrowing/examples.md index cb3ad36b4fb7..a0bb04c78eb3 100644 --- a/src/borrowing/examples.md +++ b/src/borrowing/examples.md @@ -33,9 +33,9 @@ fn main() {
-- In the first case, modifying the collection by pushing new elements into - it can potentially invalidate existing references to the collection's elements - if the collection has to reallocate. +- In the first case, modifying the collection by pushing new elements into it + can potentially invalidate existing references to the collection's elements if + the collection has to reallocate. - In the second case, the aliasing rule prevents mis-compilation: The output of `sum_and_zero` depends on the ordering of the two operations, which means if From b01299bc3b56ef169fa04fc5f234a97d9f0c959b Mon Sep 17 00:00:00 2001 From: Nicole LeGare Date: Mon, 9 Jun 2025 14:02:33 -0700 Subject: [PATCH 3/4] Rework example to compare to UB in C --- src/borrowing/examples.md | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/borrowing/examples.md b/src/borrowing/examples.md index a0bb04c78eb3..ea72de1e7fb2 100644 --- a/src/borrowing/examples.md +++ b/src/borrowing/examples.md @@ -20,14 +20,19 @@ fn main() { We can also look at a case where these rules prevent incorrect optimizations: ```rust,editable,compile_fail -fn sum_and_zero(a: &mut i32, b: &mut i32) { - *a = *a + *b; - *b = 0; +fn swap_and_increment(a: &mut i32, b: &mut i32) { + *a = *a + 1; + + let tmp = *a; + *a = *b; + *b = tmp; + + *b = *a + 1; } fn main() { - let mut x = 5; - sum_and_zero(&mut x, &mut x); + let mut x = 1; + swap_and_increment(&mut x, &mut x); } ``` @@ -37,16 +42,28 @@ fn main() { can potentially invalidate existing references to the collection's elements if the collection has to reallocate. -- In the second case, the aliasing rule prevents mis-compilation: The output of - `sum_and_zero` depends on the ordering of the two operations, which means if - the compiler swaps the order of these operations (which it's allowed to do) it - changes the result. +- In the second case, the aliasing rule prevents mis-compilation: In the C + equivalent of this function, the program produces different results when + compiled with optimzations enabled. + + - Show students [the C version of this on Godbolt][unoptimized]. By default, + with no optimizations enabled it will print `x = 3`. But + [enabling optimizations][optimized] causes it to instead print `x = 2`. - - The equivalent code in C exhibits undefined behavior, which may result in - mis-compilation and unexpected behavior, even if it doesn't cause a crash. + - Note the use of the `restrict` keyword in the C example. This tells the + compiler that `a` and `b` cannot alias, but nothing prevents you from + violating that requirement. The compiler will silently produce the wrong + behavior. + + - In the Rust version, using `&mut` automatically implies the same + restriction, and the compiler statically prevents violations of this + requirement. - Rust's aliasing rules provide strong guarantees about how references can alias, allowing the compiler to apply optimizations without breaking the semantics of your program.
+ +[unoptimized]: https://godbolt.org/z/9EGh6eMxE +[optimized]: https://godbolt.org/z/Kxsf8sahT From ef8afce41c46cbc27722aa5395dcad4a389b96e8 Mon Sep 17 00:00:00 2001 From: Nicole LeGare Date: Mon, 9 Jun 2025 14:05:41 -0700 Subject: [PATCH 4/4] Fix tyop --- src/borrowing/examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/borrowing/examples.md b/src/borrowing/examples.md index ea72de1e7fb2..acc5d4cbaea8 100644 --- a/src/borrowing/examples.md +++ b/src/borrowing/examples.md @@ -44,7 +44,7 @@ fn main() { - In the second case, the aliasing rule prevents mis-compilation: In the C equivalent of this function, the program produces different results when - compiled with optimzations enabled. + compiled with optimizations enabled. - Show students [the C version of this on Godbolt][unoptimized]. By default, with no optimizations enabled it will print `x = 3`. But