Description
Side effects, when expressed correctly, should just be functions that work for any given SideEffectRegistrar<'a>
and should themselves not be dependent upon any lifetime (i.e., the SideEffectRegistrar
itself). However, due to limitations in Rust's current implementation of higher-ranked lifetimes and some other related weird type-related bugs I've found along the way, this is not possible at the moment and the current (technically incorrect) implementation of side effects was born (they work, just not optimally). Rust just isn't quite ready for some of the odd, edge-case stuff I am trying to put it through.
Here's some sample code I was working on that properly expresses side effects, for when the time comes that it is possible:
pub trait SideEffect {
type Api<'a>;
fn build<'a>(self, registrar: SideEffectRegistrar<'a>) -> Self::Api<'a>;
}
impl<T, F: FnOnce(SideEffectRegistrar) -> T> SideEffect for F {
type Api<'a> = T;
fn build<'a>(self, registrar: SideEffectRegistrar<'a>) -> Self::Api<'a> {
self(registrar)
}
}
And for future reference, if I need it, here's some side-effect code I was experimenting with using the correct approach.
// pub fn raw<T: Send + 'static>(
// initial: T,
// ) -> impl for<'a> SideEffect<
// Api<'a> = (
// &'a mut T,
// impl Fn(Box<dyn FnOnce(&mut T)>) + Clone + Send + Sync,
// ),
// > {
// fix_lifetime(move |register: SideEffectRegistrar| register.raw(initial))
// }
// pub fn raw<T: Send + 'static>(
// initial: T,
// ) -> impl for<'a> FnOnce(
// SideEffectRegistrar<'a>,
// ) -> (
// &'a mut T,
// impl Fn(Box<dyn FnOnce(&mut T)>) + Clone + Send + Sync,
// ) {
// fix_lifetime(move |register: SideEffectRegistrar| register.raw(initial))
// }
fn fix_lifetime<F, T, R>(f: F) -> F
where
F: for<'a> FnOnce(SideEffectRegistrar<'a>) -> (&'a mut T, R),
{
f
}
pub fn as_listener() -> impl for<'a> SideEffect<Api<'a> = ()> {
|_: SideEffectRegistrar| {}
}
// TODO THis works because it's not a closure and the lifetime elision rules work correctly
pub fn zero_raw(register: SideEffectRegistrar) -> (&mut u8, impl Fn(u8) + Clone + Send + Sync) {
let (state, rebuild) = register.raw(0);
let set_state = move |new_state| {
rebuild(Box::new(move |state| *state = new_state));
};
(state, set_state)
}