Greetings,
I ran into behavior I don't understand with the (relatively) new return-position impl trait in trait feature's interaction with the borrow checker. See the following example.
The following code compiles fine (playground):
#![allow(unused)]
use std::collections::HashMap;
use std::hash::Hash;
trait Foo {
fn foo(&self) -> impl Hash + Eq;
}
impl Foo for () {
fn foo(&self) -> impl Hash + Eq {
(1i32, 3i32)
}
}
trait Bar {
type T: Hash + Eq;
fn bar(&self) -> Self::T;
}
impl Bar for () {
type T = (i32, i32);
fn bar(&self) -> Self::T {
(1, 3)
}
}
fn main() {
let mut m = HashMap::new();
let mut closure = || {
let f = ();
m.insert(f.bar(), 0);
};
closure();
}
However, if you replace
m.insert(f.bar(), 0);
with
m.insert(f.foo(), 0);
then the compilation fails:
Compiling playground v0.0.1 (/playground)
error[E0597]: `f` does not live long enough
--> src/main.rs:31:18
|
28 | let mut m = HashMap::new();
| ----- lifetime `'1` appears in the type of `m`
29 | let mut closure = || {
30 | let f = ();
| - binding `f` declared here
31 | m.insert(f.foo(), 0);
| ^------
| |
| borrowed value does not live long enough
| argument requires that `f` is borrowed for `'1`
32 | };
| - `f` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` (bin "playground") due to 1 previous error
What causes this? What causes the difference to the Bar
trait? From what I gather from the rust reference, the return type is de-sugared to be the return type in the trait implementation.
So this means, that if I change the trait implementation from:
fn foo(&self) -> impl Hash + Eq {
to
fn foo(&self) -> (i32, i32) {
the code compiles as well.
However, this is not good for real world scenarios, where you'd bound something to the trait, and then we're left with the more opaque impl Eq + Hash
type.
Why is the borrow checker assuming the return value is a reference? Moreover, even if you change the bound requirements to be Hash + Eq + Clone
, and you attempt to clone the returned value, that doesn't satisfy the borrow checker either.
I also considered the possibility that the lifetime of the type itself isn't long enough, so I tried adding 'static
to the return type bounds as well, and that didn't change anything (though I must admit I don't understand what that means well enough, anyway).
I guess the main question is, out of curiosity, is there any way around this other than falling back to the associated type syntax?