Hey there, I have run into a quite puzzling thing with lifetimes. I would be really glad if someone helped me understand why this works the way it works.
I have defined a function with the following signature, which is supposed to take a piece of data and turn into into another chunk of data that can be rendered to HTML. Post
is basically the front matter and T
can be another chunk of HTML:
pub fn post<'fm, 'md, 'post, T>(fm: &'fm Post, content: T) -> impl Renderable + 'post
where
'fm: 'post,
'md: 'post,
T: Renderable + 'md
{
And then I have another function that uses the previous function. This is supposed to grab a file, read it and then return a closure that lets you render HTML:
fn transform(meta: gen::Source) -> gen::Asset {
let loc = meta.dirs.strip_prefix("content").unwrap();
match meta.kind {
gen::SourceKind::Index => match meta.ext.as_str() {
"md" | "mdx" | "lhs" => {
let loc = loc.join("index.html");
let data = fs::read_to_string(&meta.path).unwrap();
let (fm, md) = md::preflight::<md::Post>(&data);
let call = move |assets: &[&gen::Asset]| {
let content = md::render(&md);
html::post(&fm, Raw(content)).render().into()
};
gen::Asset {
kind: gen::AssetKind::Html(Box::new(call)),
out: loc,
meta,
}
},
_ => gen::Asset {
kind: gen::AssetKind::Unknown,
out: loc.join(meta.path.file_name().unwrap()).to_owned(),
meta,
}
},
But actually, I would like to make the function generic so I could use it for different kinds of html pages, depending on the data type, so I tried creating a trait like this, which is basically 1:1 the signature of the html::post
function:
trait Transformable {
fn transform<'f, 'm, 'html, T>(&'f self, content: T) -> String
where
'f: 'html,
'm: 'html,
T: Renderable + 'm;
}
impl Transformable for md::Post {
fn transform<'f, 'm, 'html, T>(&'f self, content: T) -> String
where
'f: 'html,
'm: 'html,
T: Renderable + 'm {
html::post(self, content)
.render()
.into()
}
}
However when I try to use it like this:
fn transform<T>(meta: gen::Source) -> gen::Asset
where
T: for<'de> serde::Deserialize<'de>,
T: Transformable,
{
let loc = meta.dirs.strip_prefix("content").unwrap();
match meta.kind {
gen::SourceKind::Index => match meta.ext.as_str() {
"md" | "mdx" | "lhs" => {
let loc = loc.join("index.html");
let data = fs::read_to_string(&meta.path).unwrap();
let (fm, md) = md::preflight::<T>(&data);
let call = move |assets: &[&gen::Asset]| {
let content = md::render(&md);
T::transform(&fm, Raw(content)).into()
};
gen::Asset {
kind: gen::AssetKind::Html(Box::new(call)),
out: loc,
meta,
}
},
_ => gen::Asset {
kind: gen::AssetKind::Unknown,
out: loc.join(meta.path.file_name().unwrap()).to_owned(),
meta,
}
},
I get an error:
The Rust compiler tells me that to fix this I need to add 'static
into that functions signature like so:
fn transform<T>(meta: gen::Source) -> gen::Asset
where
T: for<'de> serde::Deserialize<'de> + 'static,
T: Transformable,
And indeed, after adding this it compiles, however I still don't know why I needed to add 'static
there and what effect it has.