Last active
August 26, 2025 09:31
-
-
Save ShawnHymel/ecbdb3a311d55f420af8bda05defa273 to your computer and use it in GitHub Desktop.
Rust ownership and borrowing rules demonstration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Demonstration of the major ownership and borrowing rules in Rust | |
| // Run with `rustc rust-ownership-examples.rs && ./rust-ownership-examples` | |
| struct SensorReading { | |
| value: u16, | |
| timestamp_ms: u32, | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 1. Each value in Rust has an owner. | |
| fn demo_ownership() { | |
| let reading = SensorReading {value: 1, timestamp_ms: 100}; | |
| println!("{}, {}", reading.value, reading.timestamp_ms); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 2. There can only be one owner at a time. | |
| fn demo_one_owner() { | |
| let reading = SensorReading {value: 2, timestamp_ms: 100}; | |
| // Transfer (move) ownership | |
| let new_owner = reading; | |
| // Error: borrow of moved value: `reading` | |
| // println!("{}", reading.value); | |
| // println!("{}", reading.timestamp_ms); | |
| // This works | |
| println!("{}, {}", new_owner.value, new_owner.timestamp_ms); | |
| } | |
| fn demo_copy() { | |
| let my_array = [1, 1, 2, 3, 5, 8]; | |
| // Primitives and arrays implement the Copy trait (if elements implement Copy) | |
| let my_copy = my_array; | |
| // Both of these work | |
| println!("{:?}", my_array); | |
| println!("{:?}", my_copy); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 3. When the owner goes out of scope, the value will be dropped. | |
| // fn print_reading(reading: SensorReading) { | |
| // // Ownership of reading is "consumed" by this function | |
| // println!("{}", reading.value); | |
| // println!("{}", reading.timestamp_ms); | |
| // // reading goes out of scope, so the value is dropped here | |
| // } | |
| fn print_reading(reading: SensorReading) -> SensorReading { | |
| // Ownership of reading is "consumed" by this function | |
| println!("{}, {}", reading.value, reading.timestamp_ms); | |
| // Fix: return reading (shorthand for "return reading;") | |
| reading | |
| } | |
| fn demo_scope_drop_value() { | |
| // New scope | |
| { | |
| let mut reading = SensorReading {value: 3, timestamp_ms: 100}; | |
| // Error: borrow of moved value: `reading` | |
| // print_reading(reading); | |
| // Fix: return reading ownership | |
| reading = print_reading(reading); | |
| // Use `reading` after a move | |
| println!("{}, {}", reading.value, reading.timestamp_ms); | |
| } | |
| // Error: cannot find value `reading` in this scope | |
| // println!("{}, {}", reading.value, reading.timestamp_ms); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 4. You can have either one mutable reference or any number of immutable references. | |
| fn print_borrowed_reading(reading: &SensorReading) { | |
| // We borrow reading instead of consuming ownership (pass by reference) | |
| println!("{}, {}", reading.value, reading.timestamp_ms); | |
| } | |
| fn demo_mutable_references() { | |
| let mut reading = SensorReading {value: 4, timestamp_ms: 100}; | |
| // References are easier than consuming and returing ownership | |
| print_borrowed_reading(&reading); | |
| // Can have any number of immutable references | |
| let immut_ref_1 = &reading; | |
| let immut_ref_2 = &reading; | |
| let immut_ref_3 = &reading; | |
| println!("{}", (*immut_ref_1).timestamp_ms); // Explicit dereference | |
| println!("{}", immut_ref_2.timestamp_ms); // Automatic dereference | |
| println!("{}", immut_ref_3.timestamp_ms); | |
| // immut_refs are no longer used, so they go out of scope (example of "non lexical lifetimes") | |
| // Only one mutable reference at a time (exclusive) | |
| let mut_ref_1 = &mut reading; | |
| // Error: cannot borrow `reading` as mutable more than once at a time | |
| // let mut_ref_2 = &mut reading; | |
| // println!("{}", mut_ref_2.timestamp_ms); | |
| // Error: cannot borrow `reading` as immutable because it is also borrowed as mutable | |
| // let immut_ref_4 = &reading; | |
| // println!("{}", immut_ref_4.timestamp_ms); | |
| // Change value in struct through the mutable reference | |
| mut_ref_1.timestamp_ms = 1000; | |
| println!("{}", reading.timestamp_ms); | |
| // mut_ref_1 is no longer used, so it goes out of scope | |
| // Now we can borrow again! | |
| let mut_ref_3 = &mut reading; | |
| mut_ref_3.timestamp_ms = 2000; | |
| println!("{}", reading.timestamp_ms); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 5. References must always be valid. | |
| // Error: cannot return reference to local variable `some_reading` | |
| // fn return_reading() -> &SensorReading { | |
| // let some_reading = SensorReading {value: 5, timestamp_ms: 100}; | |
| // &some_reading | |
| // } | |
| // Fix: return value with full ownership | |
| fn return_reading() -> SensorReading { | |
| let some_reading = SensorReading {value: 5, timestamp_ms: 100}; | |
| some_reading | |
| // More idiomatic to just return `SensorReading {value: 5, timestamp_ms: 100}` | |
| } | |
| fn demo_valid_references() { | |
| let reading = return_reading(); | |
| println!("{}, {}", reading.value, reading.timestamp_ms); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 6. If you move out part of a value, you cannot use the whole value anymore. | |
| fn demo_partial_move() { | |
| let my_tuple = ( | |
| SensorReading {value: 6, timestamp_ms: 100}, | |
| SensorReading {value: 6, timestamp_ms: 101}, | |
| ); | |
| // Partially move ownership | |
| let first_reading = my_tuple.0; | |
| // Error: borrow of moved value: `my_tuple.0` | |
| // println!("{}", my_tuple.0.value); | |
| // Error: use of partially moved value: `my_tuple` | |
| // let new_owner = my_tuple; | |
| // println!("{}", new_owner.1.value); | |
| // Can print new owner | |
| println!("{}, {}", first_reading.value, first_reading.timestamp_ms); | |
| // Can move and borrow other parts | |
| println!("{}, {}", my_tuple.1.value, first_reading.timestamp_ms); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 7. Slices are references to the whole value and follow the same borrowing rules. | |
| fn demo_slices() { | |
| let my_array = [ | |
| SensorReading {value: 7, timestamp_ms: 100}, | |
| SensorReading {value: 7, timestamp_ms: 101}, | |
| SensorReading {value: 7, timestamp_ms: 102}, | |
| ]; | |
| // Create a slice (section of the array), borrows all of my_array immutably | |
| let slice_1 = &my_array[0..1]; | |
| // Error: cannot borrow `my_array` as mutable because it is also borrowed as immutable | |
| // let slice_2 = &mut my_array[1..3]; | |
| // Fix: we can have multiple immutable references | |
| let slice_2 = &my_array[1..3]; | |
| // Print out some of our slices | |
| println!("{}, {}", slice_1[0].value, slice_1[0].timestamp_ms); | |
| println!("{}, {}", slice_2[0].value, slice_2[0].timestamp_ms); | |
| println!("{}, {}", slice_2[1].value, slice_2[1].timestamp_ms); | |
| } | |
| fn demo_split_mut() { | |
| let mut my_array = [ | |
| SensorReading {value: 7, timestamp_ms: 100}, | |
| SensorReading {value: 7, timestamp_ms: 101}, | |
| SensorReading {value: 7, timestamp_ms: 102}, | |
| ]; | |
| // Split at index 1 to borrow two mutable slices | |
| let (slice_1, slice_2) = my_array.split_at_mut(1); | |
| // Error: cannot assign to `my_array[_].timestamp_ms` because it is borrowed | |
| // my_array[0].timestamp_ms = 1234; | |
| // We can modify each slice | |
| slice_1[0].timestamp_ms = 1000; | |
| slice_2[0].timestamp_ms = 1001; | |
| slice_2[1].timestamp_ms = 1002; | |
| // slice_1 and slice_2 go out of scope here | |
| // We can access my_array again | |
| my_array[0].timestamp_ms = 1234; | |
| // Show that the original array changed | |
| println!("{}, {}", my_array[0].value, my_array[0].timestamp_ms); | |
| println!("{}, {}", my_array[1].value, my_array[1].timestamp_ms); | |
| println!("{}, {}", my_array[2].value, my_array[2].timestamp_ms); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // Main | |
| fn main() { | |
| demo_ownership(); | |
| demo_one_owner(); | |
| demo_copy(); | |
| demo_scope_drop_value(); | |
| demo_mutable_references(); | |
| demo_valid_references(); | |
| demo_partial_move(); | |
| demo_slices(); | |
| demo_split_mut(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment