Drop Flags The examples in the previous section introduce an interesting problem for Rust. We have seen that it's possible to conditionally initialize, deinitialize, and reinitialize locations of memory totally safely. For Copy types, this...
Checked Uninitialized Memory Like C, all stack variables in Rust are uninitialized until a value is explicitly assigned to them. Unlike C, Rust statically prevents you from ever reading them until you do: fn main() { let x: i32; println!("{...
Working With Uninitialized Memory All runtime-allocated memory in a Rust program begins its life as uninitialized. In this state the value of the memory is an indeterminate pile of bits that may or may not even reflect a valid state for the...
Transmutes Get out of our way type system! We're going to reinterpret these bits or die trying! Even though this book is all about doing things that are unsafe, I really can't emphasize enough that you should deeply think about finding Anot...
Casts Casts are a superset of coercions: every coercion can be explicitly invoked via a cast. However some conversions require a cast. While coercions are pervasive and largely harmless, these "true casts" are rare and potentially dangerous...
The Dot Operator The dot operator will perform a lot of magic to convert types. It will perform auto-referencing, auto-dereferencing, and coercion until types match. The detailed mechanics of method lookup are defined here, but here is a br...
Coercions Types can implicitly be coerced to change in certain contexts. These changes are generally just weakening of types, largely focused around pointers and lifetimes. They mostly exist to make Rust "just work" in more cases, and are l...
Type Conversions At the end of the day, everything is just a pile of bits somewhere, and type systems are just there to help us use those bits right. There are two common problems with typing bits: needing to reinterpret those exact bits as...
Splitting Borrows The mutual exclusion property of mutable references can be very limiting when working with a composite structure. The borrow checker (a.k.a. borrowck) understands some basic stuff, but will fall over pretty easily. It does...
PhantomData When working with unsafe code, we can often end up in a situation where types or lifetimes are logically associated with a struct, but not actually part of a field. This most commonly occurs with lifetimes. For instance, the Ite...
Drop Check We have seen how lifetimes provide us some fairly simple rules for ensuring that we never read dangling references. However up to this point we have only ever interacted with the outlives relationship in an inclusive manner. That...
Subtyping and Variance Rust uses lifetimes to track the relationships between borrows and ownership. However, a naive implementation of lifetimes would be either too restrictive, or permit undefined behavior. In order to allow flexible usag...
Higher-Rank Trait Bounds (HRTBs) Rust's Fn traits are a little bit magic. For instance, we can write the following code: struct Closure<F> { data: (u8, u16), func: F, } impl<F> Closure<F> where F: Fn(&(u8, u16)) -> &u8, { fn call(&self) ->...
Unbounded Lifetimes Unsafe code can often end up producing references or lifetimes out of thin air. Such lifetimes come into the world as unbounded. The most common source of this is taking a reference to a dereferenced raw pointer, which p...
Lifetime Elision In order to make common patterns more ergonomic, Rust allows lifetimes to be elided in function signatures. A lifetime position is anywhere you can write a lifetime in a type: &'a T &'a mut T T<'a> Lifetime positions can ap...
Limits of Lifetimes Given the following code: #[derive(Debug)] struct Foo; impl Foo { fn mutate_and_share(&mut self) -> &Self { &*self } fn share(&self) {} } fn main() { let mut foo = Foo; let loan = foo.mutate_and_share(); foo.share(); pri...
Lifetimes Rust enforces these rules through lifetimes. Lifetimes are named regions of code that a reference must be valid for. Those regions may be fairly complex, as they correspond to paths of execution in the program. There may even be h...
Aliasing First off, let's get some important caveats out of the way: • We will be using the broadest possible definition of aliasing for the sake of discussion. Rust's definition will probably be more restricted to factor in mutations and l...
References There are two kinds of references: • Shared reference: & • Mutable reference: &mut Which obey the following rules: • A reference cannot outlive its referent • A mutable reference cannot be aliased That's it. That's the whole mode...
Ownership and Lifetimes Ownership is the breakout feature of Rust. It allows Rust to be completely memory-safe and efficient, while avoiding garbage collection. Before getting into the ownership system in detail, we will consider the motiva...