Mutability Mutable data can be mutably borrowed using &mut T. This is called a mutable reference and gives read/write access to the borrower. In contrast, &T borrows the data via an immutable reference, and the borrower can read the data bu...
Borrowing Most of the time, we'd like to access data without taking ownership over it. To accomplish this, Rust uses a borrowing mechanism. Instead of passing objects by value (T), objects can be passed by reference (&T). The compiler stati...
Partial moves Within the destructuring of a single variable, both by-move and by-reference pattern bindings can be used at the same time. Doing this will result in a partial move of the variable, which means that parts of the variable will...
Mutability Mutability of data can be changed when ownership is transferred. fn main() { let immutable_box = Box::new(5u32); println!("immutable_box contains {}", immutable_box); // Mutability error //*immutable_box = 4; // *Move* the box, c...
Ownership and moves Because variables are in charge of freeing their own resources, resources can only have one owner. This prevents resources from being freed more than once. Note that not all variables own resources (e.g. references). Whe...
RAII Variables in Rust do more than just hold data in the stack: they also own resources, e.g. Box<T> owns memory in the heap. Rust enforces RAII (Resource Acquisition Is Initialization), so whenever an object goes out of scope, its destruc...
Scoping rules Scopes play an important part in ownership, borrowing, and lifetimes. That is, they indicate to the compiler when borrows are valid, when resources can be freed, and when variables are created or destroyed.
Testcase: unit clarification A useful method of unit conversions can be examined by implementing Add with a phantom type parameter. The Add trait is examined below: // This construction would impose: `Self + RHS = Output` // where RHS defau...
Phantom type parameters A phantom type parameter is one that doesn't show up at runtime, but is checked statically (and only) at compile time. Data types can use extra generic type parameters to act as markers or to perform type checking at...
Associated types The use of "Associated types" improves the overall readability of code by moving inner types locally into a trait as output types. Syntax for the trait definition is as follows: // `A` and `B` are defined in the trait via t...
The Problem A trait that is generic over its container type has type specification requirements - users of the trait must specify all of its generic types. In the example below, the Contains trait allows the use of the generic types A and B...
Associated items "Associated Items" refers to a set of rules pertaining to items of various types. It is an extension to trait generics, and allows traits to internally define new items. One such item is called an associated type, providing...
New Type Idiom The newtype idiom gives compile time guarantees that the right type of value is supplied to a program. For example, a function that measures distance in miles, must be given a value of type Miles. struct Miles(f64); struct Ki...
Where clauses A bound can also be expressed using a where clause immediately before the opening {, rather than at the type's first mention. Additionally, where clauses can apply bounds to arbitrary types, rather than just to type parameters...
Multiple bounds Multiple bounds for a single type can be applied with a +. Like normal, different types are separated with ,. use std::fmt::{Debug, Display}; fn compare_prints<T: Debug + Display>(t: &T) { println!("Debug: `{:?}`", t); print...
Testcase: empty bounds A consequence of how bounds work is that even if a trait doesn't include any functionality, you can still use it as a bound. Eq and Copy are examples of such traits from the std library. struct Cardinal; struct BlueJa...
Bounds When working with generics, the type parameters often must use traits as bounds to stipulate what functionality a type implements. For example, the following example uses the trait Display to print and so it requires T to be bound by...
Traits Of course traits can also be generic. Here we define one which reimplements the Drop trait as a generic method to drop itself and an input. // Non-copyable types. struct Empty; struct Null; // A trait generic over `T`. trait DoubleDr...
Implementation Similar to functions, implementations require care to remain generic. struct S; // Concrete type `S` struct GenericVal<T>(T); // Generic type `GenericVal` // impl of GenericVal where we explicitly specify type parameters: imp...
Functions The same set of rules can be applied to functions: a type T becomes generic when preceded by <T>. Using generic functions sometimes requires explicitly specifying type parameters. This may be the case if the function is called whe...