In my previous blog, we discussed how Rust performs memory management. Interesting as it is, the concept of ownership does come up with a few obstacles when it comes to programming. For instance, if we use a variable as a parameter to another function, Rust doesn’t allow us to access the same variable in that scope anymore. For more details as to why that happens, please refer to the previous blog. Basically, we lose the ownership to that variable in the current scope. For example, look at the code below
This print statement is a root cause of compile-time error since it’s accessing ‘s’ after the transfer of ownership. There are times when we require the ownership of a variable after we use it as an argument to another function. Doing that is possible in the current scenario too, but is too cumbersome. We may return a tuple that contains the string itself and its length but that’s not really a nice way to do it.
As a solution to this issue, Rust provides us with the concept of References. Our aim is to make sure the variable which will be an argument to the function is available after the function call. This can be achieved by not transferring the ownership and still making the argument available to the function. That’s exactly what a reference helps us do.
Have a look at the example below:
Here, instead of letting the function take ownership for the parameter, we let “input” to refer it in the implementation. Hence, throughout the program, “s” remains the owner of the value, which makes “s” accessible after the call to the function.
The idea of functions borrowing parameters is also known as Borrowing.
Now everything in Rust is immutable by default. Which means that in the above example, we cannot make changes to “input” since its immutable. In order to make it mutable, we need to pass it using “&mut” in the function declaration viz.
fn length_of_string( input: &mut String)
Here, the “input” is known as a mutable reference. Here, any changes made to the input variable will be reflected in its home scope. Which brings in a few issues as well such as a data race. A data race in when at least two pointers try to access the same data at the same time where at least one of them intends to perform a write operation and all this happens in an unsynchronized manner.
But Rust brings in another rule which helps us tackle such a situation. It only allows having only one mutable reference. Since there’s just one mutable reference, there’s no race. So we make sure that code with data races isn’t only avoidable, but won’t even compile.
However, we may have a use case that requires us to have multiple mutable references. This can be done by making sure we do them in different scopes.
So this was a brief about how to handle references in Rust, however, the above blog is not a replacement of the original context in the book. Please refer to the same in order to get more clarity on the subject. Drop a comment for any queries on the same.