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

Can't embed a variable of type DOMTree in html! macro #12

Closed
klieth opened this issue Nov 22, 2018 · 6 comments
Closed

Can't embed a variable of type DOMTree in html! macro #12

klieth opened this issue Nov 22, 2018 · 6 comments

Comments

@klieth
Copy link

klieth commented Nov 22, 2018

I was playing around with this library, which looks super cool. I'm excited to be able to have more validation in HTML. I was trying to compose a couple different chunks of HTML in the following manner:

#![feature(proc_macro_hygiene)]
extern crate typed_html;

use typed_html::dom::DOMTree;
use typed_html::html;

fn outer(body: DOMTree<String>) -> DOMTree<String> {
    html!(
        <html>
            <head>
                <title>"Sample Title"</title>
            </head>
            <body>
                { body }
            </body>
        </html>
    )
}

fn index() -> DOMTree<String> {
    outer(html!(
        <div>
            <h1>"Test"</h1>
            <p>"A test of things"</p>
        </div>
    ))
}

fn main() {
    println!("{}", index().to_string());
}

But it seems like DOMTree doesn't implement IntoIterator properly to compose these, even though it seems like it should given that you can nest html!() calls. This is the error I get:

error[E0599]: no method named `into_iter` found for type `std::boxed::Box<dyn typed_html::dom::Node<std::string::String>>` in the current scope
  --> examples/test.rs:8:5
   |
8  | /     html!(
9  | |         <html>
10 | |             <head>
11 | |                 <title>"Sample Title"</title>
...  |
16 | |         </html>
17 | |     )
   | |_____^
   |
   = note: the method `into_iter` exists but the following trait bounds were not satisfied:
           `std::boxed::Box<dyn typed_html::dom::Node<std::string::String>> : std::iter::IntoIterator`
           `&std::boxed::Box<dyn typed_html::dom::Node<std::string::String>> : std::iter::IntoIterator`
           `&mut std::boxed::Box<dyn typed_html::dom::Node<std::string::String>> : std::iter::IntoIterator`
           `dyn typed_html::dom::Node<std::string::String> : std::iter::IntoIterator`
           `&dyn typed_html::dom::Node<std::string::String> : std::iter::IntoIterator`
           `&mut dyn typed_html::dom::Node<std::string::String> : std::iter::IntoIterator`

I also tried to wrap it into vec![body].into_iterator(), giving a slightly different error:

error[E0308]: mismatched types
  --> examples/test.rs:8:5
   |
8  | /     html!(
9  | |         <html>
10 | |             <head>
11 | |                 <title>"Sample Title"</title>
...  |
16 | |         </html>
17 | |     )
   | |_____^ expected trait `typed_html::elements::FlowContent`, found trait `typed_html::dom::Node`
   |
   = note: expected type `std::boxed::Box<(dyn typed_html::elements::FlowContent<_> + 'static)>`
              found type `std::boxed::Box<dyn typed_html::dom::Node<std::string::String>>`

Is this something that is expected to work? Am I doing something wrong? Or is there some other way I should go about this sort of thing?

@bodil
Copy link
Owner

bodil commented Nov 29, 2018

Oops, you're right, looks like it's only the text node that comes with an IntoIterator implementation currently. That's definitely a bug.

@bodil bodil closed this as completed in 670fb93 Nov 29, 2018
bodil added a commit that referenced this issue Nov 29, 2018
@bodil
Copy link
Owner

bodil commented Nov 29, 2018

By the way, if you're wondering about the weird type error in your final example, see 270e3b5#diff-77e4a492dd719688667e179426fe4ba4R31

@LPGhatguy
Copy link

I'm hitting the (sort of) inverse of this problem in a couple projects I'm working on with typed-html!

fn render_main() -> DOMTree<String> {
    html!(
        <html>
            <head>
                <title>"Rustaceans Unite!"</title>
            </head>
            <body>
                { render_body() }
            </body>
        </html>
    )
}

fn render_body() -> DOMTree<String> {
    html!(
        <h1>"Hello, world!"</h1>
    )
}

This feels like a natural way to split up my conceptual components when rendering a bunch of static HTML, but the return type of render_body doesn't work out. If I change it to Box<dyn FlowContent<String>> instead (like the code snippet you linked to alludes to) everything works, but that doesn't feel so great.

I feel like impl trait could help pin down a concrete type instead here, but I can't quite pin down exactly how.

@bodil
Copy link
Owner

bodil commented Jan 31, 2019

I don't think this would ever be possible with the current design (FlowContent is more specific than DOMTree and there's no impl keyword that can change that). The only way around it (which is something I'm considering) would be to move the structural validation out of the type system and into the macro.

@JeanMertz
Copy link

Ran into the same "issue", trying to split up my html!s by having my view objects implement fn html(&self) -> DOMTree<String>, and then having them injected in the right places. Changing to Box<dyn FlowContent<String>> worked.

Neither are very elegant, I think, since you're constantly allocating heap objects in both cases, but at least the interface itself is a bit nicer if the former return type worked.

@JeanMertz
Copy link

One other thing that's somewhat related to this, is that it's impossible to use this technique for example, when you have a top-level table and you want to iterate over the rows, using a separate html macro block:

html! {
    <table>{ rows.iter().map(to_html) }</table>
}

The to_html() returns DOMTree<String>, but it fails at compile time, since its html isn't valid, without the top-level table element around it.

error[E0277]: the trait bound `typed_html::elements::tr<_>: typed_html::elements::FlowContent<std::string::String>` is not satisfied
  --> myapp/src/lib.rs:65:9
   |
65 | /         html! {
66 | |             <tr>
67 | |                 <td>
68 | |                     <div class="my-div">
...  |
74 | |             </tr>
75 | |         }
   | |_________^ the trait `typed_html::elements::FlowContent<std::string::String>` is not implemented for `typed_html::elements::tr<_>`

So I guess, in some way, for this to work, you'd have to have something like a "snippet" that can be included in an html macro, that has less compile-time restrictions, but also can't be used other than inside a html macro call?

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

4 participants