简体   繁体   中英

How do I specify an associated type with a lifetime parameter?

I have this trait representing the ability to render text (eg HTML) from a template, and an associated type TemplateData to contain the data the template needs:

trait Template {
    type TemplateData;

    fn render(&self, data: &Self::TemplateData) -> String;
}

I want to have an implementation like this:

struct MyTemplateData<'a> {
    title: &'a str,
    author: &'a str,
    last_updated: DateTime<Utc>,
    body: &'a str,
}

struct MyTemplate;

impl Template for MyTemplate {
    type TemplateData = MyTemplateData<'a>;

    fn render(&self, data: &MyTemplateData<'_>) -> String {
        /* snip */
    }
}

MyTemplateData needs a lifetime parameter because it has a &str . That lifetime only needs to last the duration of the render method since no references leave the method, so I use an anonymous lifetime for it. But as written above, this code doesn't work. The compiler complains that 'a in the associated type specification is undeclared. If I declare it ( impl<'a> Template for ... ), that still doesn't work, because then I get error[E0207]: the lifetime parameter 'a is not constrained by the impl trait, self type, or predicates.

The two ways to fix this are to add a lifetime parameter to the trait or to add it to the MyTemplate struct it's being implemented for.

  • Adding a lifetime to MyTemplate doesn't make much sense because it doesn't contain any reference members (or in fact, any members at all in this simple example).
  • Adding it to the trait doesn't seem to make sense because the lifetime really only applies to the render method, not trait-wide (for example, I could imagine having a second method like get_rendered_size that would have its own lifetime parameter).
  • Both ways have the very inconvenient drawback that lifetime annotations then have to be added wherever the trait/struct is used.

What it seems like I really need is a way to say, "the associated type will be MyTemplateData<'a> for some lifetime 'a , but just pick 'a based on whatever render needs where it's called.

Am I thinking about this right? Is there a way to do that?

You are correct, and what you are looking for is called Generic Associated Types , or GAT for short.

The only problem is they're unstable and require nightly.

With them, it looks like:

#![feature(generic_associated_types)]

trait Template {
    type TemplateData<'a>;

    fn render(&self, data: &Self::TemplateData<'_>) -> String;
}

impl Template for MyTemplate {
    type TemplateData<'a> = MyTemplateData<'a>;

    fn render(&self, data: &MyTemplateData<'_>) -> String {
        /* snip */
    }
}

However, if you cannot use nightly, you have to choose one of the options you described (it is probably better to add the lifetime to the trait, since it is not tied to an instance but to the method, ie to an implementation of the trait).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM