2. Memory and Data Management#
Understanding how this language manages memory is key to writing efficient, safe programs.
2.1. How does this language manage memory without a garbage collector like Python or manual new/delete like C++?#
Every piece of data has exactly one “owner” at any time. When the owner goes away, the data is automatically cleaned up.
Think of it like having a single key for each room. Only the person with the key can access that room. When they leave the building, the room gets locked and cleaned automatically.
Python approach (Garbage Collection):
# Python automatically cleans up when no one references the data
data = [1, 2, 3] # Python manages this memory
# When 'data' goes out of scope, garbage collector cleans up later
C++ approach (Manual Management):
// You must manually manage memory
int* data = new int[3]{1, 2, 3}; // You allocate
delete[] data; // You must remember to free
This language’s approach:
{
let data = vec![1, 2, 3]; // 'data' owns this vector
// Use data here
} // 'data' goes out of scope, memory automatically freed
When you move data from one variable to another, the ownership transfers:
let data1 = vec![1, 2, 3]; // data1 owns the vector
let data2 = data1; // ownership moves to data2
// data1 can no longer be used
// Only data2 can access the vector now
Compare to C++:
std::vector<int> data1 = {1, 2, 3};
std::vector<int> data2 = data1; // This copies the entire vector
// Both data1 and data2 exist, using twice the memory
This system prevents:
Memory leaks (forgetting to call
delete)Double-free errors (calling
deletetwice)Use-after-free bugs (accessing freed memory)
The compiler enforces these rules at compile time. You get C++ performance with Python-like memory safety.
This concept is called ownership.
2.2. How can I pass data around functions without copying it, similar to C++ references or Python object passing?#
You can let functions “look at” or “use” your data without giving away ownership. It’s like lending a book - you still own it, but someone else can read it.
Read-only access (multiple readers allowed):
fn print_length(data: &Vec<i32>) { // Function borrows data to read it
println!("Length: {}", data.len());
}
fn main() {
let my_data = vec![1, 2, 3];
print_length(&my_data); // Lend data to function
print_length(&my_data); // Can lend again
// my_data is still usable here
}
Mutable access (only one editor at a time):
fn add_item(data: &mut Vec<i32>) { // Function borrows data to modify it
data.push(4);
}
fn main() {
let mut my_data = vec![1, 2, 3];
add_item(&mut my_data); // Lend data for modification
// my_data is modified and still usable here
}
Compare to C++:
// Read-only reference
void print_length(const std::vector<int>& data) {
std::cout << "Length: " << data.size() << std::endl;
}
// Mutable reference
void add_item(std::vector<int>& data) {
data.push_back(4);
}
Compare to Python:
# Python passes object references automatically
def print_length(data):
print(f"Length: {len(data)}")
def add_item(data):
data.append(4) # Modifies the original list
Key rules:
You can have many read-only accesses at the same time
You can have only one mutable access at a time
You cannot mix read-only and mutable access
This prevents data races and ensures memory safety without runtime overhead.
This concept is called borrowing.