Borrowing reference
A borrowed reference allow you to access data without taking ownership of it. The & operator creates a borrowed reference to a variable.
If we want to pass the reference of string it can be done as follows-
Here the program uses tuple to pass the string refernce back with the length
{
let inner_variable = String::from("welcome");
let (note, note_length) = wlecome_note(inner_variable);
println!("Length of {} is {}", note, note_length);
}
fn wlecome_note(note: String) -> (String, usize){
let note_length = note.len();
(note, note_length)
}
Output-
Length of welcome is 7
This is a tedious way, instead the string refernce can be borrowed in the function, unaffecting the refernece in main block.
{
let inner_variable = String::from("welcome");
let note_length = wlecome_note(&inner_variable);
println!("Length of {} is {}", inner_variable, note_length);
}
fn wlecome_note(note: &String) -> usize{
let note_length = note.len();
note_length
}
Here & operator borrows the reference temporary into the note variable and when the it comes out of scope doesn’t affect the inner_variable in main block. This process is called as borrowing refernce.
![](https://sandeeppote.com/wp-content/uploads/2025/01/image-45.png)
Summary
- Borrowing – Access data without taking ownership of it
- Borrowing – Create reference using the borrow operator &
- When working with reference its important to understand which variable own the data
Mutable reference
In the above code we didn’t change the reference i.e. the nore variable in wlecome_note function. To do so use push_str() method.
{
let inner_variable = String::from("welcome");
let note_length = wlecome_note(&inner_variable);
println!("Length of {} is {}", inner_variable, note_length);
}
fn wlecome_note(note: &String) -> usize{
let note_length = note.len();
note.push_str(" to the Rust programming"); // Error
note_length
}
This gives a compiler error.
![](https://sandeeppote.com/wp-content/uploads/2025/01/image-46.png)
The error is data we are referencing is not mutable reference. We have to tell Rust that it is a mutable reference. For this use &mut keyword .
{
let mut inner_variable = String::from("welcome");
let note_length = wlecome_note(&mut inner_variable);
println!("Length of {} is {}", inner_variable, note_length);
}
fn wlecome_note(note: &mut String) -> usize{
note.push_str(" to the Rust programming");
let note_length = note.len();
note_length
}
Output-
Length of welcome to the Rust programming is 31
This updates the borrowed reference and main block has the updated data.
Summary-
When using a mutable reference, you cannot create other references, to prevent the data races.
If you’re only working with regular immutable references, then you can create as many of those as you want pointing to the same variable. The restriction comes in when you try to create references in addition to the one allowed mutable reference. Even if those additional references are immutable, it creates the potential for problems and the Rust compiler will not allow it.
Dangling references
A dangling reference is a reference to something that no longer exists at the referenced location.
A dangling reference can occur when a function returns a reference to a value that is owned by a variable whose scope is limited to that function.
It’s easy to erroneously create a dangling pointer—a pointer that references a location in memory that may have been given to someone else—by freeing some memory while preserving a pointer to that memory. In Rust, by contrast, the compiler guarantees that references will never be dangling references: if you have a reference to some data, the compiler will ensure that the data will not go out of scope before the reference to the data does.
{
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s // Error
}
![](https://sandeeppote.com/wp-content/uploads/2025/01/image-48.png)
Solution-
Remove borrow operator
{
let reference_to_nothing = dangle();
println!("Dangling refernce data is {}", reference_to_nothing);
}
fn dangle() -> String { // Remove borrow operator
let s = String::from("hello");
s // Remove borrow operator
}
Output-
Dangling refernce data is hello
Slices
Borrowing a data type with sequence of elements, if we only want subset of elements we use Slice. Commonly used slices is String &str
let message = String::from("Welcome to Rust");
println!("message is {}", message);
let last_word = &message[10..];
println!("last word is - {}", last_word);
Here we sliced the string to only get the last word i.e. Rust. Here the message variable is still the owner of the string. and last_word variable points to the address of the slice.
Output
message is Welcome to Rust
last word is Rust
![](https://sandeeppote.com/wp-content/uploads/2025/02/image-2.png)
&String != &str – String Type is not String Literal
With strings, a borrowed reference to a string is not equivalent to a slice from the string. They’re different data types.
Dref Coercion- The deref gives the compiler the ability to take a value of any type that implements Deref and call the deref method to get an & reference that it knows how to dereference
![](https://sandeeppote.com/wp-content/uploads/2025/02/image-3-1024x913.png)
Summary –
- Borrow the string object to slice the string &my_string[4..]
- Once you create one mutable reference to a variable you cannot create any other references to it.
- A slice only stores a pointer to the heap data, along with length information. The slice doesn’t need to keep track of capacity because the slice will never own anything on the heap.
- A string reference will point to an actual string on the stack, which in turn points to and owns the string data that lives on the heap. A slice will only store a pointer to the heap data.
Reference-
https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html