1

I'm learning Rust and the below code comes from the online book The Rust Programming Language link:

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

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

    let word = first_word(&s);

    s.clear(); // error!

    println!("the first word is: {}", word);
}

The compiler says below:

| let word = first_word(&s);
|                       -- immutable borrow occurs here

So I think "immutable borrow occurs here" is talking about &s instead of "let word". But if I change

let word = first_word(&s);

to

first_word(&s);

The compiler error disappears. So it makes me feel that "immutable borrow occurs here" is talking about "let word" instead of &s.

And if first_word's return value depends only on another string (which means first_word doesn't depends on the &s at all) as below:

fn first_word(s: &String) -> &str {
    println!("the input for first word is: {}", s);

    let s1 = "hi rust";
    let bytes = s1.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s1[0..i];
        }
    }

    &s1[..]
}

The compiler still says below:

| let word = first_word(&s);
|                       -- immutable borrow occurs here

I am very confused about what the compiler does actually for generating "immutable borrow occurs here".

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Moon
  • 361
  • 2
  • 10
  • 1
    Does this answer your question? [Cannot borrow as mutable because it is also borrowed as immutable](https://stackoverflow.com/questions/47618823/cannot-borrow-as-mutable-because-it-is-also-borrowed-as-immutable) – kmdreko Oct 19 '20 at 11:05
  • Ignore the actual content of the error messages for a moment and just focus on "error"/"no error". Do you understand why the first snippet is wrong (error), but if you change `let word = first_word(&s);` to just `first_word(&s);`, it becomes correct (no error)? I fully get the desire to understand how the borrow checker reaches its conclusions; however, if you don't understand what it's trying to protect you from, the process is going to look like a lot of arbitrary gobbledygook. (Honestly, sometimes it does anyway.) – trent Oct 19 '20 at 17:16
  • You can get pretty far with Rust by simply assuming that the borrow checker has perfect information and is always right, and trying to work backwards from that to see how your code violates a rule to cause undefined behavior. The assumption isn't fully correct, because there are some things that the borrow checker doesn't understand are OK, but *this* case is not one of them; i.e., using `word` after `s.clear()` *really does* cause UB here. – trent Oct 19 '20 at 17:20

1 Answers1

9

You should read the full error:

error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:5
   |
16 |     let word = first_word(&s);
   |                           -- immutable borrow occurs here
17 | 
18 |     s.clear(); // error!
   |     ^^^^^^^^^ mutable borrow occurs here
19 | 
20 |     println!("the first word is: {}", word);
   |                                       ---- immutable borrow later used here

"cannot borrow s as mutable because it is also borrowed as immutable" is the error message, everything else is just there to point out where the conflicting borrows occur.


And if first_word's return value depends only on another string (which means first_word doesn't depends on the &s at all) as below

That is not quite right. The function signature is what determines lifetimes. In your edited first_word, even though you're returning a str borrowed from a static string, the actual signature (without lifetimes elided) looks like

fn first_word<'a>(s: &'a String) -> &'a str {
   ...

The returned &str is bound to the lifetime of s regardless of exactly where it came from. This example just shows how a reference with a static lifetime can be shortened. And since the returned value is bound to the lifetime of s, s is considered borrowed by word.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • 1
    Nit: the lifetime stuff of first_word is simultanously saying that the output is only valid while `s` is valid and *also* that `s` is borrowed for as long as the output lives. This is why removing `word` "fixes" the issue: the result of `first_word` is immediately reclaimed, therefore the borrow ends immediately, and a new borrow can be created. – Masklinn Oct 19 '20 at 11:22