2

Here are two code snippets, but they show a different behavior and I couldn't understand what's going on in there. Their main function is identical. If the borrowing and ownership concept applies to one code (i.e to code 2) why not to another code i.e (code 1)?

code 1:

This code compiles with no errors and prompt the result.

fn main() {
    let mut s = String::from("Hello world");
    let result = first_word(&s);
    s.clear();
    println!("result:{:#?}", result);
}

fn first_word(s: &String) -> usize {
    let s = s.as_bytes();
    //println!("{:?}",s);
    for (i, &item) in s.iter().enumerate() {
        if item == 32 {
            return i;
        }
    }
    s.len()
}

Code 1 Output :

Finished dev [unoptimized + debuginfo] target(s) in 0.28s
 Running `target/debug/rust_Slices`
 result:5

Code 2: This code won't compile and gives an error.

fn main() {
    let mut s = String::from("Hello world");
    let result = first_word(&s);
    s.clear();
    println!("{:#?}", result);
}

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

Code 2 Output:

cannot borrow `s` as mutable because it is also borrowed as immutable
 --> src/main.rs:4:4
  |
3 |    let result = first_word(&s);
  |                            -- immutable borrow occurs here
4 |    s.clear();
  |    ^^^^^^^^^ mutable borrow occurs here
5 |     println!("{:#?}",result);
  |                      ------ immutable borrow later used here
E_net4
  • 27,810
  • 13
  • 101
  • 139
  • Please use `rustfmt` to format your code according to the community guidelines. You can find it in the upper right corner of [the playground](https://play.rust-lang.org). – hellow Jul 17 '19 at 07:09
  • Please read: https://stackoverflow.com/questions/40006219/why-is-it-discouraged-to-accept-a-reference-to-a-string-string-vec-vec-or – hellow Jul 17 '19 at 07:09

2 Answers2

2

Let's decompose:

// Let's build a string, which basically is a growable array of chars
let mut s = String::from("Hello world");

// now make result a slice over that string, that is a reference
// to a part of the underlying chars
let result = first_word(&s);

// now let's remove the content of the string (which of course removes
// what the slice was referring to)
s.clear();

// and let's... print it ?
println!("{:#?}", result);

Hopefully the borrow checker prevents you from doing this with this exact error:

cannot borrow s as mutable because it is also borrowed as immutable

And if you've understood this, the solution should be obvious: don't make result a window over another string but a string by itself, having its own content: change the second line to

let result = first_word(&s).to_string();

Now you can clear the source string and keep the first word. Of course to_string() isn't a costless operation so you might want to try keep the source string around in real applications.

hellow
  • 12,430
  • 7
  • 56
  • 79
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
1

The key thing here is lifetimes. By default lifetimes argument for functions with one input reference and output reference are the same (liftime elision). So compiler implicitly changes the code following way:

fn first_word<'a>(s: &'a String) -> &'a str {  // note 'a here
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

That means that the result borrows the input argument. You can explicitly make lifetimes different and eliminate error in the main but in this case first_word will not compile:

fn first_word1<'a, 'b>(s: &'a String) -> &'b str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
 --> src/main.rs:7:21
  |
7 |             return &s[0..i];
  |                     ^^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body
Zefick
  • 2,014
  • 15
  • 19