Skip to content
Select theme

How to borrow a value

Short answer: Put & in front of the value where you pass it (greet(&name)) and take a reference in the receiving type (fn greet(who: &String)). The function then reads through a pointer to the original instead of receiving the value itself, so nothing moves and your binding stays usable afterwards.

A parameter whose type is the value’s own type takes ownership of the argument. (The exception is Copy types like i32, which are duplicated instead of moved.) The call moves the value in, and the next line that mentions the caller’s variable stops compiling. From a fresh project — cargo new borrow-demo, then replace src/main.rs with the whole of this; the project name doesn’t matter, because nothing in the output depends on it:

error[E0382]: borrow of moved value: `name`
--> src/main.rs:9:29
|
6 | let name = String::from("Ada");
| ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
7 |
8 | greet(name);
| ---- value moved here
9 | println!("hello again, {name}");
| ^^^^ value borrowed here after move
|
note: consider changing this parameter type in function `greet` to borrow instead if owning the value isn't necessary
--> src/main.rs:1:15
|
1 | fn greet(who: String) {
| ----- ^^^^^^ this parameter takes ownership of the value
| |
| in this function
help: consider cloning the value if the performance cost is acceptable
|
8 | greet(name.clone());
| ++++++++
|
For more information about this error, try `rustc --explain E0382`.

The complaint is about name, not about greet. The call on the line above was the last place the binding owned anything, so the println! is reading a value that has already moved. Cloning would make the error go away; not moving in the first place is the cheaper fix.

The fix is one & in the signature and one at the call site. Again the whole file:

hello, Ada
hello again, Ada

&name is a reference to the String, and &String is its type. The same & shows up twice: once at the call site, where it creates the reference, and once in the signature, where it declares what the function expects. Inside greet, who points at the caller’s name — the text is not copied, and name is exactly as usable after the call as it was before. Passing the value by its own type instead is the move version of this same call.

A borrow isn’t only for function calls. let reader = &name; binds a reference the same way, and the original binding stays valid:

Ada, then Ada

Borrowing changes neither what is freed nor when. A Drop implementation shows where the free actually lands — again, a complete file:

reading shopping list
done reading
dropping shopping list

The drop happens at the end of main, exactly where it would have happened without the call at all: read borrowed the value, so the owner’s scope still decides when it dies. Had read taken the Note by value, dropping would have printed inside the call instead. That is the whole difference a & makes — the function gets to look, and ownership stays where it was.

One limit is worth stating now, because it shapes every borrow you will write: a reference cannot outlive the value it points at. How long a borrow counts as lasting is the subject of its own page later in this chapter, and writing through a borrow — which needs &mut and a mut binding — is the page after that.