1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
//! Examples in this module present how the concepts of *Ownership* and *Borrowing* work in
//! Rust.
//!
//! In *safe* Rust one cannot write and compile a program that has any of the following:
//! * [dangling pointers](https://en.wikipedia.org/wiki/Dangling_pointer)
//! * [use after free](https://owasp.org/www-community/vulnerabilities/Using_freed_memory)
//! * [double free](https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory)
//!
//! and other typical [memory errors](https://en.wikipedia.org/wiki/Memory_safety).
#[derive(Debug)]
pub struct Point2D {
pub x: f64,
pub y: f64,
}
pub fn take_ownership(p: Point2D) {
println!("The point is {:?}", p)
// Rust's compiler implicitly adds `drop(p)` which physically frees the memory held by `p`
}
pub fn borrow_point_immutably(p: &Point2D) {
println!("The point is {:?}", p)
}
pub fn borrow_twice(x: &Point2D, y: &Point2D) {
println!("x = {:?} and y = {:?}", x, y);
}
pub fn borrow_point_mutably(p: &mut Point2D) {
println!("The point is {:?}", p);
p.x = 3.;
println!("New point is {:?}", p);
}
#[derive(Copy, Clone, Debug)]
pub struct RGBColor(u8, u8, u8);
pub fn show_color(color: RGBColor) {
println!("The color is {:?}", color)
}
#[derive(Debug)]
pub struct Palette<'a> {
pub colors: &'a [RGBColor],
}
pub fn show_palette(palette: &Palette) {
println!("Color palette: {:?}", palette);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
// Allocate `Point2D` data on current stack frame and alias it immutably as `p`
let point = Point2D { x: 4., y: 2. };
// Move the ownership of data held by `p` to function `take_ownership`.
take_ownership(point);
// Because the memory held by `point` has been dropped inside `take_ownership`, Rust
// prevents any further usage of `point` (use after free / double free)!
// See example in `UseAfterFreeTest` and `DoubleFeeTest`.
// Allocate `Point2D` data on current stack frame and alias it immutably as `p1`
let p1 = Point2D { x: 1., y: 2. };
// This time we borrow a *shared reference* (basically a fat pointer monitored by Rust's
// Borrow Checker) to function `borrow_point_immutably`. The function gets a read-only
// access to `p1`'s memory and returns it at the end.
borrow_point_immutably(&p1);
borrow_point_immutably(&p1);
// There cant be any number of shared references to the same data as long as there is no
// mutable reference at the same time (this is known as *Aliasing XOR Mutability*).
// See the example in `AliasingXorMutabilityTest`.
borrow_twice(&p1, &p1);
// Move memory ownership from `p1` to mutable alias `p2`
let mut p2 = p1;
// Finally, a function can also borrow some value mutably as an *exclusive reference* and
// make a side-effect on these. There can either be multiple shared references to a piece
// of memory or single exclusive reference (cannot read and write at the same time).
borrow_point_mutably(&mut p2);
// Because the memory has been borrowed (even though mutably), this call compiles.
borrow_point_mutably(&mut p2);
// `assert_eq!(3., p1.x)` won't compile because the ownership of memory held by `p1`
// has been moved to `p2` - see `ExclusiveOwnershipTest`.
assert_eq!(3., p2.x);
}
#[test]
fn call_by_value() {
let color = RGBColor(128, 0, 128);
// Function `show_color` takes ownership of the color argument. However, `RGBColor` struct
// derives `Copy` trait which means that this struct is easy and fast to copy and pass by
// value (this is how primitive types such as integers and floats are handled).
show_color(color);
// Because the ownership has not been moved from `color` (rather a copy has been created
// during previous function call), following call is safe and thus compiles.
show_color(color);
}
#[test]
fn lifetimes() {
let colors = vec![RGBColor(1, 1, 1), RGBColor(2, 2, 2)];
// Lifetimes define the minimum span of how long a piece of memory must remain valid so
// that it does not outlive some other memory it references.
{
// In this example a palette references a vector of colors. This works because we've
// bound the palette lifetime to the lifetime of the color vector.
let palette = Palette { colors: &colors };
// Releasing colors (e.g. with an explicit `drop(colors);`) before this call would be
// prevented by the Borrow Checker for lifetime validation.
show_palette(&palette);
// At this point palette is dropped.
}
// Even though the palette was freed, `colors` is still points to valid memory.
for color in colors.into_iter() {
show_color(color);
}
}
}
/// This test shows that in *safe* Rust one cannot compile a code that would result in
/// [double free](https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory).
///
/// # Example
/// ```compile_fail
/// struct Data;
///
/// let data = Data;
///
/// drop(data); // Free the memory once
/// drop(data); // Free the memory again
/// ```
pub struct DoubleFeeTest;
/// This test shows that in *safe* Rust one cannot compile a code that would result in
/// [use after free](https://owasp.org/www-community/vulnerabilities/Using_freed_memory).
///
/// # Example
/// ```compile_fail
/// struct Data;
///
/// fn use_data(data: Data) {
/// // <arbitry code using the data>
/// drop(data) // Exaplicit call to `drop` the data
/// }
///
/// let data = Data;
/// // Use data once and free them afterwards
/// use_data(data);
/// // Use data again after free
/// use_data(data);
/// ```
pub struct UseAfterFreeTest;
/// This test demonstrates the **Aliasing XOR Mutability** principle enforced by Rust. This concept
/// states that at any time there can be either
/// * multiple shared references
/// * or single exclusive reference
///
/// # Example
/// ```compile_fail
/// #[derive(Debug)]
/// struct Data;
///
/// fn use_data(_data: &Data) {}
///
/// let mut data = Data;
///
/// let shared = &data;
/// let exclusive = &mut data;
///
/// // Using read-only reference while there exists a reference with exclusive access
/// use_data(shared);
/// ```
pub struct AliasingXorMutabilityTest;
/// This test demonstrates that in Rust each allocated memory has **exactly one owner**. Conversely,
/// there cannot be two owners of the same value and ownership can only be moved around.
///
/// # Example
/// ```compile_fail
/// struct Data;
///
/// fn use_data(_data: &Data) {}
///
/// let old_owner = Data;
/// use_data(&old_owner);
///
/// let new_owner = old_owner; // Value has been moved at this point
/// use_data(&new_owner);
///
/// // This is no longer allowed!
/// use_data(&old_owner);
/// ```
pub struct ExclusiveOwnershipTest;
