Open richard-uk1 opened 5 years ago
Very cool! Would be good to try and reuse bits from https://github.com/rustwasm/gloo and contribute libraries / designs / feedback back to it!
Thanks for the tip! I'll take a look.
EDIT OT but I'm having loads of fun with this lib, and it's good practice for thinking about lifetimes as well!
I've had some spare time this weekend and have come back to this. I'm making progress, and specifically I've found that I like a particular pattern:
So I'm basically mimicking the Elm Architecture(tm), and not using the Render
trait apart from the root State struct. What I want to do now is create structs for my views that look like
struct LoginPage<'bump> {
username: &'bump str,
password: &'bump str
}
where the contents of the page are borrowed from the bump arena. That way you can re-use strings/vecs in different parts of the page without reallocating them. Of course, you could do this with just functions, but I find it a lot neater to build a struct for a given view component, and then implement render for it.
So to my problem: when I do
impl<'bump> Render for LoginPage<'bump> {
fn render(&self, ctx: &mut RenderContext<'bump>) -> Node<'bump> {
//..
}
}
the compiler complains that the signature of render
doesn't match the trait (I guess because there is an extra constraint on 'bump
). What I need to tell the compiler is that I promise that the lifetime the render function will use will be shorter than the lifetime of the struct. I've tried some variations like
impl<'bump> Render for LoginPage<'bump> {
fn render<'bump2>(&self, ctx: &mut RenderContext<'bump2>) -> Node<'bump2>
where 'bump2: 'bump
{
//..
}
}
but to no avail. Can anyone give me some tips? The alternative is not using the Render
trait, then I can make the signature of render
be whatever I want.
Hey, I know this hasn't been updated in a long time, but I ran into the problem that @derekdreery mentioned pretty soon after getting started with dodrio. I was able to get it to work by tweaking the Render
trait a bit:
// moved the generic lifetime to the trait itself
pub trait Render<'a> {
/// Render `self` as a virtual DOM. Use the given context's `Bump` for
/// temporary allocations.
fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a>;
}
In addition, I was able to modify RootRender to maintain object safety by using higher-kinded (I think?) lifetimes:
pub trait RootRender: Any + for<'a> Render<'a> {
/// Get this `&RootRender` trait object as an `&Any` trait object reference.
fn as_any(&self) -> &dyn Any;
/// Get this `&mut RootRender` trait object as an `&mut Any` trait object
/// reference.
fn as_any_mut(&mut self) -> &mut dyn Any;
}
These two changes + some tweaks across the repo to support them allowed me to write this test:
#[test]
fn render_bump_scoped_child() {
use crate::{Node, Render, bumpalo::collections::String, RenderContext, builder::*};
struct Child<'a> {
name: &'a str,
}
impl<'a> Render<'a> for Child<'a> {
fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
text(self.name)
}
}
struct Parent;
impl<'a> Render<'a> for Parent {
fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
let child_name = String::from_str_in("child", cx.bump).into_bump_str();
div(&cx).children([Child { name: child_name }.render(cx)]).finish()
}
}
}
I believe this solves the problem of passing bump allocated strings into child components. I verified that all tests still pass after this change as well.
I personally don't think this adds any more lifetime complexity than before, and as far as I can tell, this doesn't appear to require much of a refactor outside of moving the <'a>
from the render function to the Render trait.
It's worth noting that these tweaks were based off of the master branch, which also includes a breaking change to the Render trait. So if everything checks out, maybe this wouldn't be a big deal to merge?
I've got these changes on my fork of dodrio, which also includes a fix for #125. I'd be happy to open a PR if you guys are interested.
Yeah, if you want to open a pull request we can take discussion there.
FWIW, for<'a>
is a "higher-order lifetime" and when used in that position, its the same as 'static
.
I really want to use this for my webdev in future. I want to define my routes as enums!! To that end, I've started implementing realworld for dodrio
Not much there yet - the routing framework (mostly stolen from the TodoMVC example) and copies of some of the templates. I intend to keep working on it and make a note of my experience in THOUGHTS.md. Just giving you a heads up.
I need sleep now.
To run