Suppose I the following rust code.
struct Source<'a> {
content: &'a str,
}
struct Final<'a> {
content: &'a str,
}
struct Intermediary<'a> {
content: &'a str,
}
impl<'a> From<Intermediary<'a>> for Final<'a> {
fn from(intermediary: Intermediary<'a>) -> Self {
Final { content: intermediary.content }
}
}
fn example<F, C>(f: F)
where
for<'a> F: FnOnce(Source<'a>) -> C,
for<'a> C: Into<Final<'a>>,{
// implementation details
}
fn call() {
example(|source| {
Intermediary { content: source.content }
});
}
This doesn’t compile with the error message:
error: lifetime may not live long enough
--> src/main.rs:51:9
|
50 | example(|source| {
| ------- return type of closure is Intermediary<'2>
| |
| has type `Source<'1>`
51 | Intermediary { content: source.content }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
I think the problem is that the for<'a>
for F
and C
refer to different lifetimes. How can I have a single for<'a>
that covers both generics?
4
You can apply a for
quantifier to multiple type bounds by using the unstable trait_alias
feature, for example:
#![feature(trait_alias)]
trait Intermediate<'a, F> = where
F: FnOnce(Source<'a>) -> Self,
Self: Into<Final<'a>>;
fn example<F, C>(f: F)
where
for<'a> C: Intermediate<'a, F>
{
// implementation details
}
One of the type variables is picked as Self
(here C
) and the trait is named appropriately. You could also make all the type variables explicit and use a dummy for Self
, but that may be confusing for the reader, see this answer.
2
The bound can actually be fully expressed by using a helper trait:
trait HelperTrait<'a>: FnOnce(Source<'a>) -> Self::Out {
type Out;
}
impl<'a, A, Out> HelperTrait<'a> for A
where
A: FnOnce(Source<'a>) -> Out,
{
type Out = Out;
}
fn example<F>(f: F)
where
for<'a> F: HelperTrait<'a, Out: Into<Final<'a>>>,
{
}
The problem is unfortunately not entirely solved because rust doesn’t always handle higher ranked closures properly. This can be fixed either by not using closures or by forcing the closure to be higher ranked by passing it through an identity function with appropriate bounds (or possibly by using the higher-order-closure crate):
fn force_higher_ranked<F>(f: F) -> F
where
F: for<'a> FnOnce(Source<'a>) -> Intermediary<'a>,
{
f
}
fn call() {
example(force_higher_ranked(|source: Source| Intermediary {
content: source.content,
}));
}
Playground