Split string and insert words into HashMap


#1

Hi! I want to split a string and insert the words into a HashMap so that I can score how often specific words are contained. But I’m always getting this error:

for word in filename.split(|c: char| !c.is_alphanumeric()) {
   |        ^^^^^^^^ borrowed value does not live long enough

This is my Code:

fn insert_filename(&mut self, filename: String) {
    for word in filename.split(|c: char| !c.is_alphanumeric()) {
        if word.len() > 1 && !self.features.contains_key(word) {
            self.features.insert(word, 1);
        }

        if let Some(score) = self.features.get_mut(word) {
            *score += 1;
        }
    }
}

I’ve tried to_owned and clone already but I think I’m misunderstanding something crucial so I would like a clear explanation so that I can avoid this kind of error in the future.


#2

It’s hard to diagnose this exactly, because you’ve removed some important information from the error and we don’t really know what Self is in this context. I can get it to work with to_owned if I just make it a function on a HashMap (playground).

(Note: I changed the logic slightly. You had both if statements executing on a new word, giving each one an initial score of 2. I put the second if statement in an else clause to prevent this.)

If I had to guess, I’d say you’re trying to store &str in the HashMap. You can’t have an owned &str, it’s an inherently borrowed type. The owned form is a String. You can’t store borrows of filename in the map because filename goes out of scope at the end of the block. If you used to_owned, but were still trying to store a &str, it wouldn’t have worked, because that would still be trying to store a borrow of a String which would be deallocated when the function returns.


#3

Wow, that was pretty simple and also a bit stupid of me. It was indeed as you guessed, I’ve tried to store a &str in a HashMap. Thanks for the explanation and the logic hint!


#4

No problem! Deciding whether to use owned vs. borrowed data in a given context can take some getting used to. For instance, you might be able to get away with filename: &str since insert_filename doesn’t appear to need ownership, but I don’t know if you’re storing the filename elsewhere in self that you aren’t showing in this example. There are use cases for borrowed HashMap keys, but it’s usually not practical/worthwhile to keep the owners alive separately from the map.


#5

Which is not stupid at all, by the way, under the right circumstances: that is, all of the references live at least as long as the map itself does. The easiest case is &'static str, but there are other instances, usually when the hashmap is constructed and used all within one function.


#6

That can definitely be useful in some cases, though I’d question the benefit if you’re generating the strings dynamically, since you’d still have to store them somewhere. You can even make the function generic over both cases (playground), though this kind of thing makes the type inferencer pack up and leave the building :wink:.