0

I'm currently learning Rust from the the book 'The Rust Programming Language' and seemingly I've encountered the following:

fn main() {
    let mut s = String::from("Hello");
    let mut add_suffix = || s.push_str(" world");
    println!("{s}");
    add_suffix();
}

results in the compiler error:

error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
 --> src/main.rs:4:16
  |
3 |     let mut add_suffix = || s.push_str(" world");
  |                          -- - first borrow occurs due to use of `s` in closure
  |                          |
  |                          mutable borrow occurs here
4 |     println!("{s}");
  |                ^ immutable borrow occurs here
5 |     add_suffix();
  |     ---------- mutable borrow later used here
  |
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

What I've understood is that add_suffix somehow took a mutable reference to s (I know that push_str requires a mutable reference) and thus increases the lifetime of add_suffix to start on the closure definition and ends on the call.

But I don't seem to understand why is that happening before we call the closure (namely add_suffix), why does Rust enforce that and what's unsafe about that? If we imagine the lifetime of that mutable reference starts and ends on the the call I can't see a way where that go wrong.

cafce25
  • 15,907
  • 4
  • 25
  • 31
Kami SM
  • 29
  • 6

1 Answers1

-1

This has nothing to do with lifetimes. As you can see in the compiler errors, lifetimes are not mentioned at all. Instead, this is a simple case of "cannot mutate and alias simultaneously". I won't delve into the specific reasons; the TLDR is "data race". Here's some useful reading if you are interested in the "why": Rust book, Mutable aliasing question.

In your example, when the closure is defined and stored in a variable, it has already captured s by mutable reference. This means until the mutable reference to s is released (a.k.a. until add_suffix goes out of scope, after line 5 in your snippet), there can exist no other references (because mutable reference is exclusive). Hence you cannot borrow s on line 4.

cyqsimon
  • 2,752
  • 2
  • 17
  • 38
  • Appreciated, I'm already into the Rust book, but what I'm seemingly don't understand is why defining a closure captures the variable in the first place. – Kami SM May 29 '23 at 21:25
  • This is not about data races, which can only occur in concurrent code. There are quite a few other reasons why Rust has its reference aliasing rules. – Mark Saving Jun 09 '23 at 22:12