3

I'm trying to use the Snowball stemmer crate in Rust to stem a vector of words. It should be simple, but the borrow checker keeps rejecting my code:

// Read user input
let input = stdin();
let mut stemmer = Stemmer::new("english").unwrap();
for line in input.lock().lines() {
    let line = line.unwrap();
    let mut query: Vec<_> = line.split_whitespace().collect();
    for t in &mut query {
        *t = stemmer.stem_str(t);
    }
    // …
}

The borrow checker says I have two mutable borrows of stemmer on the line *t = stemmer.stem_str(t); and rejects my code. (Line 80 is where the block of for line in input.lock().lines() end.)

57  18 error    E0499  cannot borrow `stemmer` as mutable more than once at a time (first mutable borrow occurs here) (rust-cargo)
57  18 error    E0499  cannot borrow `stemmer` as mutable more than once at a time (second mutable borrow occurs here) (rust-cargo)
80   5 info     E0499  first borrow ends here (rust-cargo)

If I call the stem() method directly, I get a String, but then I can't just call as_str() and expect to assign the obtained &str back to *t, since the borrow checker complains that the "borrowed value does not live long enough".

57  18 error           borrowed value does not live long enough (temporary value created here) (rust-cargo)
57  18 info            consider using a `let` binding to increase its lifetime (rust-cargo)
57  42 info            temporary value only lives until here (rust-cargo)
80   5 info            temporary value needs to live until here (rust-cargo)

I'm not sure if this has something to do with the implementation details of this library, but I really feel stuck here. I never expected stemming a vector of inputs would be so difficult.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
xji
  • 7,341
  • 4
  • 40
  • 61
  • 2
    `or_exit()` doesn't seem to be a macro, as you say in the comment above, since the `!` is missing (typo?). Also: could you add the complete compiler error? :) – Lukas Kalbertodt Dec 19 '16 at 16:41
  • @LukasKalbertodt That was something defined in the codebase, not standard library. I should probably change the code sample here simply to `unwrap()`. I now included the complete error. – xji Dec 19 '16 at 16:43

1 Answers1

4

From the documentation of stem_str:

The str reference it returns is only valid as long as you don't call stem or stem_str again; thus, Rust's borrowchecker won't let call one of them function if you have such a reference in scope.

Presumably, this is because the stemmer implementation actually has some kind of internal buffer where the word is stored as it is stemmed.

This is why you cannot call stem_str twice while keeping a reference to the string; doing so would invalidate the first string!.

I can't just call as_str() and expect to assign the obtained &str back to *t

The compiler is absolutely correct again. You are attempting to create a value, take a reference to it, store the reference, then drop the value! That's a memory vulnerability and you can't do it.

Instead, collect a vector of Strings:

for line in input.lock().lines() {
    let line = line.unwrap();
    let mut query: Vec<_> = line.split_whitespace()
        .map(|t| stemmer.stem(t))
        .collect();
}

I'd highly recommend reading The Rust Programming Language and understanding how references work and what they prevent. Do this before and during getting into anything complicated with ownership. These chapters specifically:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I've read the book but apparently those concepts still take some time to digest. With your method I am able to get a vector of `String`s, however there is some function that expects `Vec<&str>`. Is there a convenient way to convert `Vec` to `Vec<&str>`, or would it be more idiomatic to avoid writing functions that require `Vec<&str>` and change its input type to `Vec`? – xji Dec 19 '16 at 17:10
  • @JIXiang I'd also suggest searching on SO for questions: [Convert Vec to Vec<&str>](http://stackoverflow.com/q/33216514/155423). You can change your function type to accept either `&str` or `String`, if useful. – Shepmaster Dec 19 '16 at 17:22
  • Right. I Googled it first but I seem to have made the mistake of writing "Vector" instead of "Vec" and encountered no relevant results from Google. Thanks for pointing this question out. – xji Dec 19 '16 at 17:40
  • @JIXiang also, if you wrote those methods, you may want to [consider making them take a slice](http://stackoverflow.com/q/40006219/155423). – Shepmaster Dec 19 '16 at 18:37