Hello, I want to have State that I know will never borrow from View, but I can't convince the borrow checker. Adding 'static bound, which should make it clear that State cannot borrow, doesn't help.
trait View {
type State: 'static;
// initialize the state
fn build_state(&self) -> Self::State;
// `view` uses `self` and `state` to do computations. Return type omitted.
fn render_tree(&self, state: &mut Self::State);
}
impl View for () {
type State = ();
fn build_state(&self) -> Self::State {
()
}
fn render_tree(&self, _state: &mut Self::State) {}
}
fn view_fn<'a>(_: &'a ()) -> impl View + use<'a> {
()
}
fn main() {
let view = view_fn(&());
let mut state = view.build_state();
for _ in 0..10 {
let unit = ();
let view = view_fn(&unit);
view.render_tree(&mut state);
}
}
Error:
1 error[E0597]: `unit` does not live long enough
--> src/main.rs:26:28
|
25 | let unit = ();
| ---- binding `unit` declared here
26 | let view = view_fn(&unit);
| ^^^^^ borrowed value does not live long enough
27 | view.render_tree(&mut state);
28 | }
| - `unit` dropped here while still borrowed
29 | }
| - borrow might be used here, when `view` is dropped and runs the destructor for type `impl View`
|
= note: values in a scope are dropped in the opposite order they are defined
For more information about this error, try `rustc --explain E0597`.
error: could not compile `play` (bin "play") due to 1 previous error
but a type doesn't have drop glue is NOT necessarily Copy. we need something like !Drop as bound (if Drop is default like Sized. alternatively, we can make Drop an explicit bound and !Drop the default, either way should be usable), but we are not there (yet?).
or, (although it's probably not possible for your use case, since you use an explicit use<> capture list, but just in case,) if you can make the view_fn()NOT capture the lifetime, then it doesn't matter whether the hidden type has drop glue.
the real reason of the error message is the type checker needs to unify the type of variable view in the outer scope and the view in the inner loop.
let's give the opaque type returned by view_fn() a name, say UnitVIew<'a>, the problem is, <UnitView<'a> as View>::State (the type of the state variable in the outer scope) and <UnitView<'b> as View>::State (the expected type of the argument of view.render_tree(state) in the inner loop) is NOT the same type, unless 'a and 'b is the exact same lifetime (subtyping is not sufficient).
consider make State a generic parameter instead of associated type.
That's not possible, because each View requires some data that it defines itself.
And they compose. Generics generics are anyway only achievable with associated types, so it will end up with associated types even if there somehow were generics. But this is beyond the issue.
Turns out, if State: 'staticAND drop the view, it compiles. I tried both but separately, for example with that view: I skipped the view.build_state() entirely and just used unsafe { zeroed() } (code can't compile anyway), but error remained . Now while trying to improve the example (to show that removing view not helps in practic) I discovered that it actually helps if State: 'static. I can afford to combine those things. Thank you all for help!
As a workable solution as found, this probably isn't of interest, but I found a way to indirectly express this via traits and bounds on stable without modifying the original trait. So I'll throw it out there anyway:
Probably only works when Arg: 'static due to the usual HRTB woes.
Thanks, but it just occurred to me that it is probably useless to you in practice because of the annoying closure inference problems...
fn view_maker_alt() -> impl UnifiedMaker<str> {
fn le_sigh<F: Fn(&str) -> &str>(f: F) -> F { f }
// `le_sigh` was required to guide inference as the `Fn` supertrait
// bound on `UnifiedMarker` does not override inference like a direct
// bound does (and the compiler will not figure out that you wanted to
// return something with the same lifetime as a closure argument without
// that override).
le_sigh(|s| s)
}
...since a requirement of yours was not having to name the return types.