x is a HashSet that contains leaked slices of Foo's. The strings inside Foo are also leaked. (ignore the fact that i don't insert any values into the set, that doesn't matter for this compile error);
I expect that the returned type of x.get to be Option<& &'static [Foo<'static>]> no matter what is borrowed type is. But the borrow checker seems to be agree?
use std::collections::HashSet;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct Foo<'a> {
inner: &'a str
}
fn main () {
let x: HashSet<&'static [Foo<'static>]> = HashSet::new();
let str = String::from("Hello");
let foo: &[Foo] = &[Foo{inner: &str}];
let y: Option<& &'static [Foo<'static>]> = x.get::<&[Foo]>(foo);
}
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:10:24
|
10 | let foo: &[Foo] = &[Foo{inner: &str}];
| ^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
11 | let y: Option<& &'static [Foo<'static>]> = x.get::<&[Foo]>(&foo);
| --------------------------------- type annotation requires that borrow lasts for `'static`
12 | }
| - temporary value is freed at the end of this statement
error[E0597]: `str` does not live long enough
--> src/main.rs:10:36
|
9 | let str = String::from("Hello");
| --- binding `str` declared here
10 | let foo: &[Foo] = &[Foo{inner: &str}];
| ^^^^ borrowed value does not live long enough
11 | let y: Option<& &'static [Foo<'static>]> = x.get::<&[Foo]>(&foo);
| --------------------------------- type annotation requires that `str` is borrowed for `'static`
12 | }
| - `str` dropped here while still borrowed
Here's actually a simple example with the same issue
use std::collections::HashSet;
fn main () {
let x: HashSet<&'static str> = HashSet::new();
let str = String::from("Hello");
let y: Option<& &'static str> = x.get::<&str>(&&*str);
}
It's not a borrow checker bug. The borrow checker just enforces limitations from the type signatures of HashMap's API.
The return type of the call to get is indeed always Option<& &'static [Foo<'static>]>. But the parameter to get needs to fit the key type of the HashSet. The standard library defines this function signature:
impl<T, S, A> HashSet<T, S>
where
T: Eq + Hash,
S: BuildHasher,
{
pub fn get<Q>(&self, value: &Q) -> Option<&T>
where
T: Borrow<Q>,
Q: Hash + Eq + ?Sized;
}
so for your HashSet<&'static [Foo<'static>]>:
T = &'static [Foo<'static>]
you further speficy the argument Q of get to be &[Foo]. The only Borrow implementation that matches &[Foo<'_>]: Borrow<&'static [Foo<'static>] is the generic one Borrow<T> for T which means that the lifetimes are forced to actually match.
Hence the compiler complains both about foo not living long enough as well as the variable str not living long enough (two errors for the two lifetime parameters).
The Borrow trait setup can allow us to dereference once, matching Q = [Foo<'_>] but that still leaves the problem for the lifetime 'a within your Foo<'a>.
The problem is that e.g. &'static [Foo<'static>] doesn't implement Borrow<&'a [Foo<'b]>, it implements Borrow<&'static [Foo<'static>]>. The lifetime error is about what you're passing to .get, not about the return type. It's a weakness of the traits used to ensure your query value is hash-compatible with your key type.
Due to the types involved (slices, which are foreign type to your crate), a newtype approach may be the cleanest way forward.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct Key(&'static [Foo<'static>]);
impl<'foo> Borrow<[Foo<'foo>]> for Key {
fn borrow(&self) -> &[Foo<'foo>] {
self.0
}
}
fn main () {
let x: HashSet<Key> = HashSet::new();
let str = String::from("Hello");
let foo: &[Foo] = &[Foo{inner: &str}];
let y: Option<&'static [Foo<'static>]> = x.get(foo).map(|k| k.0);
}
Playground.
damn,,,
I think I'll just use unsafe instead lol.
Thanks!