Code sample from the rust book

Hi all,

Here is a code sample from the rust book (Slices Chapter):

#![allow(unused_variables)]
fn main() {
    let mut s = String::from("hello world");

    let word = first_word(&s);

    s.clear(); // Error!
}



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[..]
}

This is meant to produce an error - because we’re trying to clear a mutable reference - but it compiles just fine…

Anybody knows why?

It doesn’t compile:

error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
 --> src/main.rs:7:5
  |
5 |     let word = first_word(&s);
  |                            - immutable borrow occurs here
6 | 
7 |     s.clear(); // Error!
  |     ^ mutable borrow occurs here
8 | }
  | - immutable borrow ends here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

https://play.rust-lang.org/?gist=1e79e74aa6336d61ff79de33732a798d&version=stable&mode=debug&edition=2015

Very strange...
on my machine it compiles (Pycharm with rust plugin...)

It compiles with nightly if I turn on the Non-Lexical Lifetimes feature (nll): https://play.rust-lang.org/?gist=b6d9f45b831ccac143d7317524cc23a5&version=nightly&mode=debug&edition=2015

but I don’t see anything in your code to indicate you’ve done that… I don’t know anything about Pycharm though!

1 Like

If you’re using nightly, then it will be using the Rust 2018 preview, which turns NLL on. is edition = "2018" in your Cargo.toml?

1 Like

I am using 2018 - Thanks, I’ll go back to the stable version.

How is NLL allowing the mutable reference in s.clear()? How can it guarantee that there are no immutable references after let word = first_word(&s);?

My best guess is that the compiler determines word can be dropped before calling s.clear() since it is not used after s.clear(). Meaning it makes the code look like this:

#![allow(unused_variables)]
fn main() {
    let mut s = String::from("hello world");

    {
        let word = first_word(&s);
        // Word will be dropped here.
    }

    s.clear(); // Error!
}

I am not entirely sure this is the right reasoning though.

1 Like

Yup, this is the correct reasoning.