Lieftime issue implementation of `Function` is not general enough

I'm trying to build something that takes a closure of variable arity and allowed argument types and return types.

I have made a lot of progress, but it only works for owned types e.g. i64, String, Vec<_>. But I also want the arguments to be able to be references like &str.

Playground

I'm running in to issues where my Function trait seems to work but I can't write the function that takes the closure correctly.

Currently I've written it like this

fn takes_any_function<F, R, A>(f: F) -> Box<DynFunction>
where
    F: for<'a> Function<'a, R, A> + Send + Sync + 'static,
    R: FromReturn,
    A: for<'a> FunctionArgs<'a>,
{
    Box::new(move |args: &[Value]| -> Result<Value> {
        let args = FunctionArgs::from_values(args)?;
        Ok(FromReturn::from_return(Function::call_func(&f, args)))
    })
}

And mostly it works fine except in the case with a reference

// works
takes_any_function(|x: bool| if x { 1 } else { 2 });

// works
takes_any_function(|x: bool, y: String| ());

// does not work
let f = |x: &str| x.to_lowercase();
takes_any_function(f);
//  ^^^^^^^^^^^^^^^^^^ implementation of `Function` is not general enough

// this works though?
Box::new(move |args: &[Value]| -> Result<Value> {
    let args = FunctionArgs::from_values(args)?;
    Ok(FromReturn::from_return(Function::call_func(&f, args)))
});
error: implementation of `Function` is not general enough
  --> src/main.rs:16:5
   |
16 |     takes_any_function(f);
   |     ^^^^^^^^^^^^^^^^^^ implementation of `Function` is not general enough

Is it possible to work around this? Perhaps with an intermediate trait somewhere?

I just copy/paste full error:

   Compiling playground v0.0.1 (/playground)
error: implementation of `Function` is not general enough
  --> src/main.rs:16:5
   |
16 |     takes_any_function(f);
   |     ^^^^^^^^^^^^^^^^^^ implementation of `Function` is not general enough
   |
   = note: `[closure@src/main.rs:15:13: 15:39]` must implement `Function<'0, std::string::String, (&str,)>`, for any lifetime `'0`...
   = note: ...but it actually implements `Function<'1, std::string::String, (&str,)>`, for some specific lifetime `'1`

error: could not compile `playground` due to previous error

I wrote an answer in the other thread:

1 Like

This:

impl<'a, Func, R, T> Function<'a, R, (T,)> for Func
where
    Func: Fn(T) -> R,
    R: FromReturn,
    T: IntoArg<'a>,

Is similar to here. For fn(&str) -> String aka for<'any> fn(&'any str) to meet this bound, it needs to be coerced into a fn(&'singular_lifetime str) -> String as &'any str is not a type (and T must be a singular type). [1]

You can maybe make it work using something along these lines, but also beware that inference really fell apart, and the size of code exploded, when I tried to adapt that approach to multiple parameters.


  1. From the T: IntoArg<'a> bound, we then get that the 'singular_lifetime must be 'a. ↩︎

1 Like

Compiling but not-cleaned-up version. You can see that inference has gone out the window. I didn't understand the entire design from the start and this could be cleaned up a bit before trying poke at the inference problems. It's probably pretty much the same situation as in that other thread I linked to though.

Guess I should summarize the changes: I added associated types to IntoArg and FunctionArgs, so that GAT-like indirection could be added for &str. For the higher-typedness, IntoArg is implemented for a placeholder type Str instead of &'each str. [1] FunctionArgs needs the associated type to go from the higher-ranked placeholder to the lifetime-carrying concrete type (from Str to some &'specific str).

Then it was largely an exercise in adjusting the bounds and signatures to use the associated types, and and then annotating everywhere to guide inference.


  1. I originally implemented for the standard str, but it turns out you need this to be sized as a placeholder elsewhere. ↩︎

1 Like

Less painful inference at the cost of exploding code length version (still not paricularly cleaned up).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.