I've been learning rust and I've been trying to learn the borrow checker works but i've come across this two examples that i don't understand why only one of those is considered borrowed :
fn main() {
let mut x = String::from("aa ab");
let y = first_word(&x);
x.clear(); //Error cannot borrow X
println!("{y}");
}
//Returns an i32 reference
fn first_word(s: &String) -> &i32 {
return &32;
}
fn main() {
let mut x = String::from("aa ab");
let y = first_word(&x);
x.clear(); //Everything is fine
println!("{y}");
}
//Returns an i32
fn first_word(s: &String) -> i32 {
return 32;
}
Is it possible for someone to explain why only the second one works?
Rust does not look into function to understand how they work on the outside. The function signature must contain all necessary information.
The signature fn first_word(s: &String) -> &i32
says "takes a reference to a string" (by the way, it's practically never useful to have a &String
, always use &str
instead), and return a reference to an int. But Rust also needs lifetime information, i.e. some bounds on how long the things behind the references live.
The way this works is a very simple process called lifetime elision. For "takes a reference, returns a reference", the sane assumption is that whatever is returned is somehow related to what's passed in, so the full signature becomes fn first_word<'a>(s: &'a String) -> &'a i32
.
This means that the compiler, when it sees a call to first_word
, will assume that as long as you keep the returned reference around (the y
in your code), the thing passed in is still borrowed.