Reputation: 2402
I am trying to achieve the following:
Given an array slice of str
s:
Vec<&str>
(primarily because that is the output of .graphemes
)Vec<Vec<&str>>
(again, the &str
choice is because of .graphemes
, the vector of vectors is intentional.The error returned is that candidate_graphemes
references a temporary value. I kind of understand why, but I would appreciate an explanation, and I'm not entirely sure how to fix it. I'm fairly new to rust...
use std::collections::HashSet;
use unicode_segmentation::UnicodeSegmentation;
pub fn anagrams_for<'a>(word: &str, possible_anagrams: &[&'a str]) -> HashSet<&'a str> {
let sorted_possible_anagrams = possible_anagrams
.iter()
.map(|candidate| {
let mut candidate_graphemes = candidate
.to_lowercase()
.graphemes(true)
.collect::<Vec<&str>>();
candidate_graphemes.sort();
candidate_graphemes
})
.collect::<Vec<Vec<&str>>>();
let mut results = HashSet::new();
results
}
Upvotes: 1
Views: 596
Reputation: 605
If you look in your map closure, you can see that you are using the .to_lowercase()
method on a &str
which returns a String
.
This string belongs to the closure.
The allocation is needed because the &str
is a shared reference therefor you cannot mutate the str
directly through it.
When you call the .graphemes
method, you get back an iterator of &str
but those references are actually different form the first one: their lifetime is constrained inside of the closure while the lifetime of your original references (the &str
s in possible_anagrams
) have the lifetime 'a
(defined in your function's generic parameters).
/* ... */ .map(|candidate| {
let lowercase_string = candidate.to_lowercase(); // here is the `String`
let graphemes = lowercase_string.graphemes(true); // Here is the `Iterator<Item = &'b str>` where 'b is the lifetime of `lowercase_string`
let mut vec = graphemes.collect::<Vec<&str>>()
vec.sort();
vec // The lifetime 'b ends here (so `lowercase_string` gets dropped) but you're trying to move references to this `String` out of the closure.
}) /* ... */
This is why your code doesn't compile.
A possible fix would be to return String
s
let mut candidate_graphemes = candidate
.to_lowercase()
.graphemes(true)
.map(|s| String::from(s)) // add this
.collect::<Vec<String>>(); // `String`s do not hold any lifetimes, they are simply moved out of the closure
And all the returned strings would be lowercased. A downside to this is that all those String
s would need to be allocated on the heap. This would make your function much slower.
Another fix would be to ask possible_anagrams
to always contain lowercased &str
s. This would prevent your map from needing to allocate String
s for each candidate/grapheme.
let mut candidate_graphemes = candidate
// .to_lowercase() we know candidate is already lowercase
.graphemes(true)
.collect::<Vec<&str>>(); // now, the `&str`s have the correct lifetime of `'a`
candidate_graphemes.sort();
candidate_graphemes
Upvotes: 3