How can I specify a lifetime for closure arguments

2019-08-30 15:46发布

问题:

Playpen link: http://is.gd/EpX6lM

I have a closure that takes a slice and returns a subslice of it. Compiling the following code on rust-1.0.0-beta-2 fails:

trait OptionalFirst {
    fn optional_first<'a>(&self, x: &'a [usize]) -> &'a [usize];
}

impl<F> OptionalFirst for F where F: Fn(&[usize]) -> &[usize] {
    fn optional_first<'a>(&self, x: &'a [usize]) -> &'a [usize] {
        (*self)(x)
    }
}

fn main() {
    let bc: Box<OptionalFirst> = Box::new(
        |x: &[usize]| -> &[usize] {
            if x.len() != 0 {
                &x[..1]
            }
            else {
                &x[..0]
            }
        }) as Box<OptionalFirst>;

    let a: [usize; 3] = [1, 2, 3];
    let b: &[usize] = bc.optional_first(&a);
    println!("{:?}", b);
}

I know how to define a lifetime in a closure's type (using for <'a>), but I don't know how to specify it in the closure's implementation.

回答1:

Your implementation impl<F> OptionalFirst for F where F: Fn(&[usize]) -> &[usize] is expecting a bound lifetime parameter, for the constraint F: Fn(&[usize]) -> &[usize] is, expanded to full form: F: for<'a> Fn(&'a [usize]) -> &'a [usize].

That is, at the time you call the function, it will determine what values to select for the lifetime (they are generics).

A closure, however, cannot have any bound lifetime parameters; they are by stuck using concrete lifetime parameters. They lack the facility to wire output lifetimes to input lifetimes generically as you want: they are by very design concrete and not generic. I haven’t thought about this in great depth, but it might be possible to counteract this for generic lifetime parameters; it is not, however, something that is implemented as far as I am aware.

If you want something like this, try using a function rather than a closure. When you’re not using any of the environment there’s no benefit to using closures beyond the typically lower verbosity.

Here’s what you end up with:

fn bc(x: &[usize]) -> &[usize] {
    if x.len() != 0 {
        &x[..1]
    } else {
        &x[..0]
    }
}

Playpen



回答2:

You can create a closure with bound lifetime parameters, you just have to get the compiler to infer that type correctly. It can be done like this:

fn main() {
    let bc: Box<Fn(&[usize]) -> &[usize]> = Box::new(
        |x| {
            if x.len() != 0 {
                &x[..1]
            }
            else {
                &x[..0]
            }
        });

    let a: [usize; 3] = [1, 2, 3];
    let b: &[usize] = bc(&a);
    println!("{:?}", b);
}

However, what I don't know is how to cast it further into Box<OptionalFirst>.