Ownership
Rust's unique memory management model - the ownership system.
Ownership
Ownership is one of Rust's most important and unique features. This system guarantees memory safety without using a garbage collector.
Principles
- Every value has one "owner"
- There can only be one owner at a time
- When the owner goes out of scope, the value is freed
Scope and Ownership
RUST{ // s doesn't exist here let s = String::from("hello"); // s is valid from here // use s } // s goes out of scope, memory is freed
Move Semantics
RUSTlet s1 = String::from("hello"); let s2 = s1; // s1's value moves to s2 // s1 is no longer usable! println!("{}", s1); // ERROR!
Copy Types
Some types are automatically copied (Copy trait):
RUSTlet x = 5; let y = x; // x's value is copied, not moved println!("{}", x); // OK, x is still usable
Copy types: integers, bool, char, tuples (if all elements are Copy).
Code Examples
Scope and Ownership
How ownership works in scopes
fn main() {
let s = String::from("hello");
println!("s value: {}", s);
{
let inner = String::from("world");
println!("inner value: {}", inner);
}
println!("s is still valid: {}", s);
}Explanation:
Variable lifetimes are tied to scope. When a variable goes out of scope, memory is automatically freed.
Move Semantics
How the move operation works
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("s2 value: {}", s2);
}Explanation:
The String type is not Copy, so when we assign s1 to s2, the value "moves". s1 is no longer usable after this.
Copy Types
Automatic copying of Copy types
fn main() {
let x = 5;
let y = x;
println!("x: {}", x);
println!("y: {}", y);
let tup = (1, 2, 3);
let tup2 = tup;
println!("tup: {:?}", tup);
}Explanation:
Integers and other Copy types are automatically copied, not moved. Both variables remain usable.
Blockchain: Ownership of Transactions
Understanding ownership in blockchain transactions
struct Transaction {
from: String,
to: String,
amount: u64,
}
fn process_transaction(tx: Transaction) {
println!("Processing: {} -> {} ({} tokens)", tx.from, tx.to, tx.amount);
}
fn main() {
let tx = Transaction {
from: String::from("0xAlice"),
to: String::from("0xBob"),
amount: 100,
};
process_transaction(tx);
}Explanation:
In blockchain, transactions should be consumed when processed. Rust's ownership system enforces this - once a transaction is moved, it can't be used again, preventing accidental double-processing.
Exercises
Ownership Practice
Create a String, assign it to another variable, and try to use the original!
Starter Code:
fn main() {
let text = String::from("Rust");
}