Rust Ownership and Borrowing
sai umesh
Posted on July 18, 2019
Before we talk about Rust Ownership and Borrowing, I would like you to know that techempower has released round 18 results last week and Rust has clearly dominated in 4 of 5 categories, which shows how powerful Rust has become over the years.
Rust is a very powerful programming language but, learning Rust is not easy. it has a steep learning curve. The first thing you would find difficult to learn in Rust is probably going to be Ownership and Borrowing. Today we will try to learn basics of it.
This article is aimed at learning basics of it so, use this code in production with caution.
Rust neither has manual memory management nor garbage collector. Then the next obvious question would be, how does Rust manage memory 💭💭. Here comes Rust's most unique point, Ownership and Borrowing. let's see how does it work with some examples.
before we see any black and blue text, we need know about two types of variables:
- Static allocated types ex: int
- Heap Allocated types ex: array(Vector in Rust)
For any statically allocated types, Rust Ownership and Borrowing doesn't apply but for heap allocation it does apply. let's checkout some examples.
fn do_something(input: i32) -> i32 {
input * 2
}
fn main() {
let number: i32 = 1;
let result = do_something(number);
println!("result is {} for number {}", result, number)
}
if you run above program you would get following output, as expected.
result is 2 for number 1
now let's replace i32 (statically allocated) with String (Heap allocation) in above example.
fn do_something(input: String) -> String {
// do something with string
input
}
fn main() {
let name: String = String::from("Hello!!");
let result = do_something(name);
println!("result is {} for string {}", result, name)
}
once you run above program you will encounter first O & B problem as follows.
--> src\main.rs:11:52
|
9 | let name: String = String::from("Hello!!");
| ---- move occurs because `name` has type `std::string::String`, which does not implement the `Copy` trait
10 | let result = do_something(name);
| ---- value moved here
11 | println!("result is {} for string {}", result, name)
| ^^^^ value borrowed here after move
Any heap allocated variable will have it's scope, and generally the scope is of lifetime of it's function(ownsership). but, if you pass any heap variable to another function, not only the variable but also it's scope will be passed to that function(ownsership) along with it's lifetime. this means, main() will not have access to name of type String once it's passed down to do_something(). once do_something() is done with it's execution, input variable of type String memory will be dropped. This is how Rust maintains memory management without managing manually.
Now, To solve above problem, we will send reference of the string but not actual variable.
fn do_something(input: &String) -> String {
// do something with string
input.to_string()
}
fn main() {
let name: String = String::from("Hello!!");
let result = do_something(&name);
println!("result is {} for string {}", result, name)
}
if you run above program you would get following output.
result is Hello!! for name Hello!!
any variable in rust is immutable unless say otherwise. now let's try to mutate input and let's see what happens.
fn do_something(input: &String) {
input.push_str("world!!")
}
fn main() {
let name: String = String::from("Hello!!");
do_something(&name);
println!("result is {} for name", name)
}
if you run above program you would get following output.
error[E0596]: cannot borrow `*input` as mutable, as it is behind a `&` reference
--> src\main.rs:3:5
|
2 | fn do_something(input: &String) {
| ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
3 | input.push_str("World!!")
| ^^^^^ `input` is a `&` reference, so the data it refers to cannot be borrowed as mutable
since borrowed variable not mutable, we cannot mutate it. to solve the problem we need to make both original and passed variable mut mutable.
fn do_something(input: &mut String) {
input.push_str(" World!!")
}
fn main() {
let mut name: String = String::from("Hello!!");
do_something(&mut name);
println!("result is {} for name", name)
}
if you run above program you would get following output.
result is Hello!! World!! for name
I hope you enjoyed this article. If you have any doubt or help, you can contact me on twitter.
In next article we will talk about, how to create sharable variables between the threads.
Thanks for reading. Have a Good day!
Posted on July 18, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.