Reputation: 3211
I'm having trouble understanding why rust doesn't like my remove_str method in there:
use std::cell::RefCell;
use std::collections::HashSet;
#[derive(Hash, Eq, PartialEq)]
struct StringWrap<'a>{
s: &'a String,
}
struct Container<'a>{
m: HashSet<StringWrap<'a>>
}
impl<'a> Container<'a>{
fn remove_str(&mut self, s: &str){
let string = String::from(s);
let to_remove = StringWrap{s: &string};
self.m.remove(&to_remove);
}
}
It chokes with:
error[E0597]: `string` does not live long enough
--> tests/worksheet.rs:17:39
|
14 | impl<'a> Container<'a>{
| -- lifetime `'a` defined here
...
17 | let to_remove = StringWrap{s: &string};
| ^^^^^^^ borrowed value does not live long enough
18 | self.m.remove(&to_remove);
| ------------------------- argument requires that `string` is borrowed for `'a`
19 | }
| - `string` dropped here while still borrowed
As far as I can see, my string
and to_remove
live long enough to allow the .remove
call to do its job. Is it because remove is potentially asynchronous or something like that?
Thanks for any help or insight!
Upvotes: 4
Views: 388
Reputation: 23
In context of HashSet<T>::remove/take/get(arg1: &Q):
These call internally will try to get "T's borrowed version Q from inside the HashSet<T> itself" to compare to arg1 by Borrow<Q> trait for T. So a custom impl Borrow<Q> for T
with appropriate lifetime is a good idea.
Upvotes: -1
Reputation: 125865
As far as I can see, my string and to_remove live long enough to allow the .remove call to do its job. Is it because remove is potentially asynchronous or something like that?
No, it's because HashSet::remove
must be called with something that the item becomes when borrowed:
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool
where
T: Borrow<Q>,
Q: Hash + Eq,
However, unless you manually implement Borrow
for StringWrap
, only the blanket reflexive implementation will apply—and thus remove
can only be called with value
of type &StringWrap<'a>
. Note the lifetime requirement.
What you need to do to make this work is to implement Borrow
for StringWrap
. You could, for example, do the following:
impl Borrow<str> for StringWrap<'_> {
fn borrow(&self) -> &str {
self.s
}
}
and then Container::remove_str
can merely forward its argument to HashMap::remove
:
impl Container<'_> {
fn remove_str(&mut self, s: &str) {
self.m.remove(s);
}
}
All that said, it's rather unusual to store references in a HashSet
: typically one would move ownership of the stored String
s into the set, which would render this problem moot as no lifetimes would be at play.
Upvotes: 4