Rust Programming
Object Ownership and Moving
Memory un-safety and bugs usually come from dynamically allocated objects on the heap that are not successfully reclaimed (memory leak), or is freed more than once.
The solution that Rust has come up with is the idea of ownership, where you can’t have multiple variables reference the same heap objects simultaneously
This trivial example shows how a heap object (or value) can be moved from one variable to another
fn main(){
//allocates a dynamic, mutable string on the heap
let mut s1 = String::new();
//here s1 can be used and modified
// object (value) to be moved from s1 to s2
let mut s2 = s1;
//here s1 is no longer a valid reference to the object
//here s2 goes out of scope, and heap memory reclaimed
}
This less trivial example shows how ownership can be moved into an out of function calls
fn main(){
//allocate object on the heap
let s1 = String::new();
//declare an uninitialized String variable
let s2: String;
//moving ownership to the function, then function moves ownership back to variable s2
s2 = add_data(s1);
//variable s1 no longer a valid reference to the object
//s2 goes out of scope, and memory gets reclaimed
}
fn add_data(st: String) -> String {
st.push_str("Some Data");
return st;
}
Object Borrowing and References
Passing a reference to an object doesn’t move ownership. There are two kinds of reference passing
Passing an immutable reference
Passing a mutable reference
Here is an example of both
fn main() {
//heap object
let mut s1 = String::new();
borrow_and_modify(&mut s1);
//here s1 is still valid, as ownership was never moved, only borrowed
borrows_object(&s1);
} // here s1 goes out of scope, and thus destroyed and reclaimed
// declared to borrow an object that would be mutated
fn borrow_and_modify(st: &mut String){
st.push_str("Adding some Data");
}
// declared to borrow an object that would not be mutated in anyway
fn borrows_object(st: &String){
println!("sring length is {}",st.len());
}
Notice where the mut keyword needs to be used
In variable declaration, to indicate that the object is mutable
In the declaration of a borrowing function params that would be mutated within the function
When passing a reference to an object to a borrowing function that intends to mutate the object
Primative Borrowing and dereferencing
Since primative types are generally stored on the stack, and can easily and cheaply be copied, they need to be dereferenced when being passed and mutated within functions
fn main() {
let mut x: u8 = 0; //needs to be declared mutable
increment(&mut x); //pass a mutable reference to a borrowing function
println!("current value: {}",x);
}
//defines a borrowing function that mutates a primitive
fn increment(d: &mut u8){
*d = *d + 1; //dereferencing is required here
}