Skip to content

Commit b4ce545

Browse files
committed
Unsafe superpowers
1 parent 6ecee20 commit b4ce545

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

content/lessons/15_unsafe/index.md

+23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,29 @@ weight = 1
66
lesson_date = 2024-12-19
77
+++
88

9+
# Unsafe Rust Alter Ego
10+
11+
So far, no operation in Rust that we performed could trigger UB (Undefined Behaviour):
12+
13+
- data races were prevented by _sharing XOR mutability_ borrow checker rule;
14+
- use-after-free, dangling references, etc. were prevented by lifetimes & ownership.
15+
16+
But no respectable, powerful programming language can stand being constrained that much, in such a cage!
17+
18+
In Rust, `unsafe` keyworld unleashes the hidden superpowers.
19+
20+
### Unsafe superpowers
21+
22+
Inside a `unsafe { ... }` block, you can (and normally you can't):
23+
24+
- **Dereference a raw pointer,**
25+
- **Call an unsafe function or method,**
26+
- Access or modify a mutable static variable,
27+
- Implement an unsafe trait,
28+
- Access fields of a union.
29+
30+
The first superpower is the most important. (Efficient) implementation of many data structures would be impossible without ability to use raw pointers, as references don't allow circular dependencies, among other limitations.
31+
932
## Reading
1033

1134
- [The Book, Chapter 19.1](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#![allow(unused_assignments)]
2+
#![allow(unused_variables)]
3+
#![allow(dead_code)]
4+
5+
/* unsafe superpower 1: dereferencing pointers. */
6+
fn superpower_1() {
7+
let x = 42;
8+
9+
// Implicit &T -> *const T conversion.
10+
let raw_ptr: *const i32 = &x;
11+
12+
// An old way to directly create a pointer.
13+
let raw_ptr: *const i32 = std::ptr::addr_of!(x);
14+
15+
// The new way to directly create a pointer.
16+
let raw_ptr: *const i32 = &raw const x;
17+
18+
// Dereferencing a raw pointer requires an `unsafe` block.
19+
println!("Value: {}", unsafe { *raw_ptr });
20+
}
21+
22+
/* unsafe superpower 2: calling an unsafe function. */
23+
unsafe fn unsafe_function() {
24+
println!("This is an unsafe function!");
25+
}
26+
27+
fn superpower_2() {
28+
unsafe {
29+
// Calling an unsafe function.
30+
unsafe_function();
31+
}
32+
}
33+
34+
/* unsafe superpower 3: Accessing or modifying mutable static variable.
35+
* It is unsafe because it can lead to data races if accessed concurrently.
36+
* */
37+
38+
static mut COUNTER: i32 = 0;
39+
40+
fn increment_counter() {
41+
unsafe {
42+
// Accessing and modifying a mutable static variable
43+
COUNTER += 1;
44+
println!("Counter: {}", COUNTER);
45+
}
46+
}
47+
48+
fn superpower_3() {
49+
// This would cause UB: a data race.
50+
// std::thread::spawn(increment_counter);
51+
increment_counter();
52+
}
53+
54+
/* unsafe superpower 4: Implementing unsafe traits.
55+
* It is unsafe because safe code is permitted to cause UB if an unsafe trait
56+
* is implemented for a type that should not implement it (think Send/Sync).
57+
* */
58+
59+
unsafe trait CanBeAtomic {
60+
fn safe_method_of_unsafe_trait(&self);
61+
}
62+
63+
struct MyStruct {
64+
i: i32,
65+
}
66+
67+
unsafe impl UnsafeTrait for MyStruct {
68+
fn safe_method_of_unsafe_trait(&self) {
69+
println!("Method called!");
70+
}
71+
}
72+
73+
fn superpower_4() {
74+
let my_struct = MyStruct { i: 42 };
75+
76+
// Calling a safe method from an unsafe trait
77+
my_struct.safe_method_of_unsafe_trait();
78+
}
79+
80+
/* unsafe superpower 5: Accessing fields of a union.
81+
* It is unsafe because union can contain a different variant that we try to read,
82+
* so we could read some rubbish value.
83+
* */
84+
85+
union MyUnion {
86+
int_value: i32,
87+
bool_value: bool,
88+
}
89+
90+
fn main() {
91+
let u = MyUnion { int_value: 42 };
92+
93+
unsafe {
94+
// Accessing a field of a union
95+
println!("Union value as int: {}", u.int_value);
96+
97+
// Would result in UB, as the compiler may assume that bool is either 0 or 1 underneath.
98+
// println!("Union value as bool: {}", u.bool_value);
99+
}
100+
}

0 commit comments

Comments
 (0)