utkarshkukreti / markup.rs

A blazing fast, type-safe template engine for Rust.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Owned types as parameters

drdo opened this issue · comments

Maybe I misunderstood something, but it seems that markup.rs always adds a reference to whatever types you use.

Consider the following code:

markup::define! {
    Foo<X: Render, I: Iterator<Item = X>>(items: I) {
        @for i in items {
            p { @i }
        }
    }
}

This does not compile, it complains that Iterator is not implemented for &I.

Why is this the case? Why can't I just pass the type I itself?

What would be the correct way to implement something similar to the code above?

Hi,

This is because of how the Render trait is implemented: a template can be rendered multiple times after it's created once. If the fields of the template were available directly inside the template, this would not be possible as all the fields would need to be moved out of the struct on the first render.

What kind of values will you be passing to this template? One solution could be to accept Cloneable iterators (many iterators in Rust are cheap to clone):

markup::define! {
    Foo<X: markup::Render, I: Iterator<Item = X> + Clone>(items: I) {
        @for i in items.clone() {
            p { @i }
        }
    }
}

fn main() {
    println!(
        "{}",
        Foo {
            items: vec![1, 2, 3].iter()
        }
    );
}

Cloning an iterator in this case is extremely cheap (just a couple of pointers IIRC).

I'm wondering. Why would a template need to be rendered multiple times?

Can you not just create and consume a new instance each time?

I don't remember exactly, but I think the primary reason I went for this approach is because otherwise templates couldn't implement std::fmt::Display because Display::fmt takes &self.

Alright, thanks.