Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic iterator field for a template #881

Open
livingsilver94 opened this issue Oct 12, 2023 · 6 comments
Open

Generic iterator field for a template #881

livingsilver94 opened this issue Oct 12, 2023 · 6 comments

Comments

@livingsilver94
Copy link

I need to compute the string values out of a slice of Messages by filtering and mapping. At the end of the chain, I have an std::iter::Map<...> which can be seen as an impl Iterator<Item = String>. At this point I cannot figure out a way to pass this iterator to a Template to loop over it:

#[derive(Template)]
#[template(path = "enum.c.j2")]
struct Enum<'a, E: Iterator<Item = String>> {
    count_prefix: String,
    entries: &'a E,
    object_name: String,
}

The compiler returns:

error[E0507]: cannot move out of a shared reference
  --> lib/src/generator/c.rs:14:10
   |
14 | #[derive(Template)]
   |          ^^^^^^^^
   |          |
   |          value moved due to this method call
   |          move occurs because value has type `E`, which does not implement the `Copy` trait
   |
note: `into_iter` takes ownership of the receiver `self`, which moves value
  --> /builddir/build/BUILD/rustc-1.72.1-src/library/core/src/iter/traits/collect.rs:271:18
   = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)

Is there any way to do this, or am I forced to compute the entire list up front?

@djc
Copy link
Owner

djc commented Oct 12, 2023

What does the relevant template code look like?

@livingsilver94
Copy link
Author

Tree:

[Lit(Lit { lws: "", val: "typedef enum\n{", rws: "\n    " }), Loop(Loop { ws1: Ws(None, None), var: Name("entry"), iter: Var("entries"), cond: None, body: [Lit(Lit { lws: "\n        ", val: "SWM_CANPDU_", rws: "" }), Expr(Ws(Some(Suppress), Some(Suppress)), Var("entry")), Lit(Lit { lws: "", val: ",", rws: "\n    " })], ws2: Ws(None, None), else_nodes: [], ws3: Ws(None, None) }), Lit(Lit { lws: "\n    ", val: "", rws: "" }), Comment(Comment { ws: Ws(None, None), content: "{#" }), Lit(Lit { lws: "\n    ", val: "SWM_CANPDU_", rws: "" }), Expr(Ws(Some(Suppress), Some(Suppress)), Var("count_prefix")), Lit(Lit { lws: "", val: "_NUMBER_ID,\n}\nSWM_CANPDU_", rws: "" }), Expr(Ws(Some(Suppress), Some(Suppress)), Var("object_name")), Lit(Lit { lws: "", val: "List_priv_t;", rws: "" })]

Code:

impl < 'a, E : Iterator < Item = String > > ::askama::Template for Enum< 'a, E > {
    fn render_into(&self, writer: &mut (impl ::std::fmt::Write + ?Sized)) -> ::askama::Result<()> {
        include_bytes! ("/home/path/lib/templates/enum.c.j2") ;
        writer.write_str("typedef enum\n{\n    ")?;
        {
            let mut _did_loop = false;
            let _iter = (&self.entries).into_iter();
            for (entry, _loop_item) in ::askama::helpers::TemplateLoop::new(_iter) {
                _did_loop = true;
                ::std::write!(
                    writer,
                    "\n        SWM_CANPDU_{expr0},\n    ",
                    expr0 = &::askama::MarkupDisplay::new_unsafe(&(entry), ::askama::Html),
                )?;
            }
            if !_did_loop {
            }
        }
        ::std::write!(
            writer,
            "\n    \n    SWM_CANPDU_{expr1}_NUMBER_ID,\n}}\nSWM_CANPDU_{expr2}List_priv_t;",
            expr1 = &::askama::MarkupDisplay::new_unsafe(&(self.count_prefix), ::askama::Html),
            expr2 = &::askama::MarkupDisplay::new_unsafe(&(self.object_name), ::askama::Html),
        )?;
        ::askama::Result::Ok(())
    }
    const EXTENSION: ::std::option::Option<&'static ::std::primitive::str> = 
    Some("c")
    ;
    const SIZE_HINT: ::std::primitive::usize = 
    126
    ;
    const MIME_TYPE: &'static ::std::primitive::str = 
    "text/plain; charset=utf-8"
    ;
}
impl < 'a, E : Iterator < Item = String > > ::std::fmt::Display for Enum< 'a, E > {
    #[inline]
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        ::askama::Template::render_into(self, f).map_err(|_| ::std::fmt::Error {})
    }
}

@djc
Copy link
Owner

djc commented Oct 12, 2023

I just mean the contents of enum.c.j2.

@livingsilver94
Copy link
Author

Oh sorry about that. Here it is:

typedef enum
{
    {% for entry in entries %}
        SWM_CANPDU_{{- entry -}},
    {% endfor %}
    {# The last entry of the enum is the entry count. #}
    SWM_CANPDU_{{- count_prefix -}}_NUMBER_ID,
}
SWM_CANPDU_{{- object_name -}}List_priv_t;

@livingsilver94
Copy link
Author

livingsilver94 commented Oct 13, 2023

I guess I could have {% for entry in entries.clone() %} in the template, since cloning std::iter::Map is cheap given its 32-bit size. I'm open to more elegant solutions though, if any.

@djc
Copy link
Owner

djc commented Oct 13, 2023

The notion of an &impl Iterator itself feels fairly confused, since you need a &mut reference to actually iterate. One thing that could work is to have the field be more like messages: &[Message<'a>] and then expose an iter(&self) method on Enum which yields the owned iterator, for use in the template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants