audulus / rui

Declarative Rust UI library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

can we nest states using only references?

wtholliday opened this issue · comments

I was looking into using references instead of passing around a context, and it seems tricky. Just wanted to record somewhere how far I got.

This is close, but doesn't quite work:

    struct State<F, V> {
        f: F,
        state: String,
        phantom: std::marker::PhantomData<V>,
    }

    impl<'a, V: View + 'a, F: Fn(&'a String) -> V + 'a> View for State<F, V> {
        fn draw(&self) {
            (self.f)(&self.state).draw();
        }
    }

The problem is in F: Fn(&'a String) -> V + 'a. This says the entire closure cannot outlive the String passed in. That's not quite what we want. It's ok if the closure lives longer. What we want is for the return type (V) to live as long as the String passed in.

What we want is something like F: for<'a> Fn(&'a String) -> V<'a> where V is a higher-kinded-type, but rust doesn't (yet) have this feature.

This works, at the cost of boxing views, and process has to return a new value because it can't get a mutable reference:

    trait View {
        fn draw(&self);
        fn process(&self, data: &String) -> Option<String>;
    }

    struct State<F> {
        f: F,
    }

    impl<F> View for State<F>
    where
        F: for<'a> Fn(&'a String) -> Box<dyn View + 'a>,
    {
        fn draw(&self) {
            // Get the state from somewhere.
            let s = "hello world".to_string();
            (self.f)(&s).draw();
        }

        fn process(&self, _data: &String) -> Option<String> {
            let mut s = "hello world".to_string();
            let r = {
                let v = (self.f)(&s);
                v.process(&s)
            };

            if let Some(n) = r {
                s = n;
            }
            None
        }
    }

    struct Empty {}

    impl View for Empty {
        fn draw(&self) {}
        fn process(&self, _data: &String) -> Option<String> {
            None
        }
    }

    fn state<'b, F: 'b + for<'a> Fn(&'a String) -> Box<dyn View + 'a>>(f: F) -> Box<dyn View + 'b> {
        Box::new(State { f })
    }