Example: References
fn main() {
let mut boxed = Box::new(123);
let a: *mut i32 = &mut *boxed as *mut i32;
let b: *mut i32 = std::ptr::null_mut();
println!("{:?}", *a);
println!("{:?}", b.as_mut());
}
Confirm understanding of the syntax
-
Box<i32>type is a reference to an integer on the heap that is owned by the box. -
*mut i32type is a so-called raw pointer to an integer that the compiler does not know the ownership of. Programmers need to ensure the rules are enforced without assistance from the compiler.- Note: raw pointers do not provide ownership info to Rust. A pointer can be semantically owning the data, or semantically borrowing, but that information only exists in the programmer’s mind.
-
&mut *boxed as *mut _expression:*boxedis …&mut *boxedis …- finally,
as *mut i32casts the reference to a pointer.
-
References, such as
&mut i32, “borrow” their referent. This is Rust’s ownership system.
Confirm understanding of ownership
-
Step through code:
- (Line 3) Creates raw pointer to the
123by dereferencing the box, creating a new reference and casting the new reference as a pointer. - (Line 4) Creates raw pointer with a NULL value
- (Line 7) Converts the raw pointer to an Option with
.as_mut()
- (Line 3) Creates raw pointer to the
-
Highlight that pointers are nullable in Rust (unlike references).
-
Compile to reveal the error messages.
-
Discuss
- (Line 6)
println!("{:?}", *a);- Prefix star dereferences a raw pointer.
- It is an explicit operation. Whereas regular references have implicit dereferencing most of the time thanks to the Deref trait. This is referred to as “auto-deref”.
- Dereferencing a raw pointer is an unsafe operation.
- Requires an unsafe block.
- (Line 7)
println!("{:?}", b.as_mut());as_mut()is an unsafe function.- Calling an unsafe function requires an unsafe block.
- (Line 6)
-
Demonstrate: Fix the code (add unsafe blocks) and compile again to show the working program.
-
Demonstrate: Replace
as *mut i32withas *mut _, show that it compiles.- We can partially omit the target type in the cast. The Rust compiler knows
that the source of the cast is a
&mut i32. This reference type can only be converted to one pointer type,*mut i32.
- We can partially omit the target type in the cast. The Rust compiler knows
that the source of the cast is a
-
Add safety comments:
- We said that the unsafe code marks the responsibility shift from the compiler to the programmer.
- How do we convey that we thought about our unusual responsibilities while writing unsafe code? Safety comments.
- Safety comments explain why unsafe code is correct.
- Without a safety comment, unsafe code is not safe.
-
Discuss: Whether to use one large unsafe block or two smaller ones:
- Possibility of using a single unsafe block rather than multiple.
- Using more allows safety comments as specific as possible.
Suggested Solution
fn main() { let mut boxed = Box::new(123); let a: *mut i32 = &mut *boxed as *mut i32; let b: *mut i32 = std::ptr::null_mut(); // SAFETY: `a` is a non-null pointer to i32, it is initialized and still // allocated. println!("{:?}", unsafe { *a }); // SAFETY: `b` is a null pointer, which `as_mut()` converts to `None`. println!("{:?}", unsafe { b.as_mut() }); }