Reputation: 8237
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.
Upvotes: 3
Views: 633
Reputation: 430358
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 String
s:
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:
Upvotes: 4