Hey, hoping someone can help. I'm working on some code where I end up wrapping closures and using the parameters to the outer functions while inside the inner functions. So for a simple example, here is one where we pass a string into an outer function and it returns a closure that will print that string while returning its own input unmodified:
fn simple_wrap<'o>(outer: &'o str) -> impl (for<'i> FnMut(&'i str) -> &'i str) + 'o {
move |inner: &str| {
println!("{}", outer);
inner
}
}
That works great, but I'm having a problem when I go up to 3 levels. So instead of an outer
, and inner
we're going to have an outer
, middle
and inner
but do essentially the same thing:
fn wrap_function<'o>(
outside: &'o str,
) -> impl (for<'m> FnMut(&'m str) -> Box<dyn 'm + for<'i> FnMut(&'i str) -> &'i str>) + 'o {
move |middle: &str| {
// Works:
println!("{:?}", outside);
Box::new(move |inner: &str| {
// Breaks if uncommented:
// println!("{:?}", outside);
println!("{:?}", middle);
inner
})
}
}
As you can see, I've managed to get the outer lifetime 'o
added to the outer closure, but not to the inner closure. I think what I want to do is change:
Box<dyn 'm + ...
to
Box<dyn 'm + 'o + ...
but if I do that then I get:
error[E0226]: only a single explicit lifetime bound is permitted
I also tried adding 'o
as a dependency to 'm
with
impl (for<'m: 'o> ...
but then you get:
error: lifetime bounds cannot be used in this context
So I'm pretty sure what I need is for that 'o
to end up added to the lifetime of the inner closure, but I don't know how to do that. Link to the rust playground.
Then this is a bit of a stretch so it may be more of a distraction from my simplified question above, but the next thing I tried was changing the return type to an impl Trait
that I was hoping to use to capture both the middle and inner lifetimes so that I could just add the outer lifetime as the single explicit lifetime bound. So I added in:
Wrapped
which is the inner closure:
trait Wrapped<'i> {
type Output: 'i;
fn call(&mut self, input: &'i str) -> Self::Output;
}
impl<'i, D: 'i, F> Wrapped<'i> for F
where
F: FnMut(&'i str) -> D,
{
type Output = D;
fn call(&mut self, arg: &'i str) -> D {
self(arg)
}
}
and Wrapper
which is the outer closure returning an inner closure depending on the outer closure's parameter:
trait Wrapper<'i, 'o, O> {
type InnerOutput;
type Inner: 'o + Wrapped<'i, Output = Self::InnerOutput>;
type Outer: 'o;
fn call(&mut self, previous_match: Self::Outer) -> Self::Inner;
}
impl<'i, 'o, F, O: 'o, I: 'o + Wrapped<'i>> Wrapper<'i, 'o, O> for F
where
F: FnMut(O) -> I,
{
type InnerOutput = I::Output;
type Inner = I;
type Outer = O;
fn call(&mut self, previous_match: Self::Outer) -> Self::Inner {
self(previous_match)
}
}
which if we make a function that takes in that type:
fn wrapped_acceptor<AC>(mut accepted: AC)
where
for<'i, 'o> AC: Wrapper<'i, 'o, &'o str, Outer = &'o str, InnerOutput = &'i str>,
{
println!("accepted.")
}
then we can call
wrapped_acceptor(wrap_function("foo"));
and everything compiles fine, proving that the return type to wrap_function
does successfully map to Wrapper
but if I then try to leverage that type by changing the return type of wrap_function
to be an impl Wrapper
it also does not work.
fn wrap_function_impl_wrapper<'x>(
outside: &'x str,
) -> impl (for<'i, 'o> Wrapper<'i, 'o, &'o str, Outer = &'o str, InnerOutput = &'i str>) + 'x {
move |middle: &str| {
println!("{:?}", outside);
Box::new(move |inner: &str| {
// println!("{:?}", outside);
println!("{:?}", middle);
inner
})
}
}
The context, in case it helps, is I'm working on writing a nom parser that is going to support a look-behind-like functionality where the previous match get passed in and used to decide how the parser functions (so, for example, to do things like match a "*" but only if its preceded by a space). Thats working, but now I'm trying to combine multiple look-behind parsers together to do something like "attempt to match using any look-behind parser in this list" (in nom alt()
) and thats why I'm getting into 3-levels deep and hitting this 1 explicit lifetime bound constraint.
One last playground link with that broken code attempting to use impl Wrapper
commented out.