Why i need mut for modify borrowed value?

Why s must be mutable? in my mind borrowing a value is not have any connection with original variable. Adding mut on the original variable looks like change the var.

Is this about heap? so the original value from var s after pushed string literal is change. Using references is not mean i make a copy but using pointer from actual resource on the heap memory.

fn main() {
    let mut s = String::from("hello");
    change(&mut s);

fn change(some_string: &mut String) {
    some_string.push_str(", world");

Well, the mutable reference could be used to modify s, so you need to mark s with mut to allow that.

1 Like

ahh i see thanks.

But I guess you would not think that way about borrowing a book from someone. Think good old fashioned paper book.

Perhaps they let you borrow it on the condition that you do not write in it or damage it in anyway. That would be an immutable borrow of the book.

Perhaps they let you borrow it with permission to write it it or mark it up some how. Say you are proof reading it and want to mark up mistakes. That would be a mutable borrow of the book.

Why think differently about Rust values?


No, it's about mut versus non-mut variable bindings. Without mut you can't overwrite the variable or take a &mut to it. Also, the &mut s points to s, not directly to the heap memory containing the textual data.[1]

 &mut s          s [stack]                      [heap]
+------+        +-----+-----+----------+       +---+---+---+---+---+
| 0x...|  --->  | ptr | len | capacity |  ,--> | h | e | l | l | o |
+------+        +-----+-----+----------+  |    +---+---+---+---+---+
[stack]            `----------------------'

Your push modifies the value on the stack in addition to writing to the heap -- at a minimum it changes the length. In the case of needing to grow the allocation, it also changes the capacity and probably the pointer to the heap as well.

Or here's a version that definitely overwrites all of s on the stack, in case that's easier to understand.

fn change(some_string: &mut String) {
    *some_string = String::new();

(The old value will be dropped resulting in a deallocation in this example.)

  1. References don't know or "care" if they're pointing at the heap, the stack, static data, or whatever. ↩ī¸Ž


It absolutely is connected with the original variable. That's the whole point. If it weren't, that would be a copy, not a borrow.

This has nothing to do with the heap. I don't know where people get the idea from that this somehow only applies to heap-allocated values. The borrow checker doesn't even have a concept of heap. References to heap-allocated data and stack-allocated data don't have different types. A reference is a reference, and it can point anywhere.


Thanks you guys, you're reply is very on point :innocent:

Looks like the required mut annotation did its job very well, as changing the var is exactly what will happen :slight_smile:

A reference in Rust will always avoid (the overhead of [implicitly]) copying the data. If you want to have a separate copy that you can mutate without overhead, then you need to make a copy (usually via .clone()) which can then be passed by value. If you don't want the original changed but don't need to mutate anything either then immutable references (&T) work fine.

In some cases, you might not know in advance whether or not you want to mutate the data. To spare the overhead of copying in case you don't mutate, but keep the original untouched if you do, there is the concept of "copy on write" of copying the data only if/once you want to mutate something - which in Rust can be done using two types: Cow<'_, T> is a type for doing copy-on-write while the borrow checker is still responsible for the non-copying case (treating it like a &T reference) - and there's Rc<T> and Arc<T> in case you don't want any connection, even in lifetime, to your original value (which would also be wrapped in an Rc or Arc respectively), and those offer copy-on-write functionality via their .make_mut() methods.


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.