I need to store a fn(I) -> O
(where I
& O
can be references) in a 'static
struct. O
needs to be a trait with an 'static
generic associated type, that associated type is also stored in the struct. Neither I
nor O
itself get stored inside of the struct, so their lifetime shouldn't matter. But the compiler is still complaining about I
not living long enough.
trait IntoState {
type State: 'static;
fn into_state(self) -> Self::State;
}
impl IntoState for &str {
type State = String;
fn into_state(self) -> Self::State {
self.to_string()
}
}
struct Container<F, S> {
func: F,
state: S,
}
impl<I, O> Container<fn(I) -> O, O::State>
where
O: IntoState,
{
fn new(input: I, func: fn(I) -> O) -> Self {
// I & O lives only in the next line of code. O gets converted into
// a `'static` (`String`), that is stored in `Container`.
let state = func(input).into_state();
Container { func, state }
}
}
fn map(i: &str) -> impl '_ + IntoState {
i
}
fn main() {
let _ = {
// create a temporary value
let s = "foo".to_string();
// the temporary actually only needs to live in `new`. It is
// never stored in `Container`.
Container::new(s.as_str(), map)
// ERR: ^ borrowed value does not live long enough
};
// ERR: `s` dropped here while still borrowed
}
playground
As far as I can tell, the compiler's error message is misleading, what it actually requires is an explicitly defined associated type:
fn map(i: &str) -> impl '_ + IntoState<State = String> {
i
}
The excellent answer given to the quesion: Why does the compiler not infer the concrete type of an associated type of an impl trait return value? provides enough information on why this is actually needed.
See also Rust issue #42940 - impl-trait return type is bounded by all input type parameters, even when unnecessary
You can use a generic type parameter instead of returning an impl
in which case you don't have to specify the associated type:
fn map<T: IntoState>(i: T) -> T {
i
}
Apparently there is still some confusion about what exactly is going on here, so I'll try to destil my comments into a short answer.
The problem here is the prototype of the function map()
:
fn map(i: &str) -> impl '_ + IntoState
This specifies that the return type of map()
is some type implementing IntoState
, with an unspecified associated type State
. The return type has a lifetime parameter with the lifetime of the argument i
; let's call that lifetime 'a
, and the full return type T<'a>
. The associated type State
of this return type now is <T<'a> as IntoState>::State
, which is parametrized by 'a
. The compiler is currently not able to eliminate this lifetime parameter from the assoicated type, in spite of the 'static
declaration in the trait definition. By explicitly specifying the associated type as String
, the compiler will simply use the explicitly specified type String
instead of <T<'a> as IntoState>::State
, so the lifetime parameter is gone, and we don't get an error anymore.
This compiler shortcoming is discussed in this Github issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With