匿名函数 - 由于需求冲突,无法推断出合适的生命周期

Anonymous function - cannot infer an appropriate lifetime due to conflicting requirements

我正在尝试使用策略模式来使用不同的方法从输入列表生成操作。

use rand::{prelude::SliceRandom, Rng};

#[derive(Debug, Clone)]
struct Inner {
    num: usize,
}

#[derive(Debug)]
enum Outer {
    ActionA(Inner),
    ActionB(Inner),
}

fn main() {
    // A method to generate an Outer from a list of Inners
    type Strategy = Box<dyn Fn(&Vec<&Inner>) -> Box<dyn FnMut() -> Outer>>;

    let random_strategy: Strategy = Box::new(|inners| {
        let mut rng = rand::thread_rng();
        Box::new(move || {
            let inner = inners.choose(&mut rng).unwrap();
            if rng.gen_bool(1.0 / 2.0) {
                Outer::ActionA(inner.to_owned().clone())
            } else {
                Outer::ActionB(inner.to_owned().clone())
            }
        })
    });

    let inners = vec![&Inner { num: 3 }, &Inner { num: 4 }];
    let get_choice = random_strategy(&inners);

    for _ in 0..4 {
        let choice = get_choice();
        println!("{:?}", choice);
        // do something...
    }
}

每个策略都是一个闭包,因为它可能包含一些状态(为了简化示例,此处未显示)。

我收到编译器错误

cannot infer an appropriate lifetime due to conflicting requirements
expected `(&&Vec<&Inner>, &mut ThreadRng)`
   found `(&&Vec<&Inner>, &mut ThreadRng)`
but, the lifetime must be valid for the static lifetime...
expected `Box<(dyn FnMut() -> Outer + 'static)>`
   found `Box<dyn FnMut() -> Outer>`

我很困惑为什么输出 Outer 必须具有静态生命周期。我正在创建 Outer 一次,使用完全拥有的 Inner (克隆后),从匿名函数返回它,并且再也不会在该匿名函数中使用它。我希望调用者 (let choice = get_choice()) 拥有 Outer.

的所有权

This 问题有相同的错误但有所不同,因为在我的示例中,Outer 需要拥有 Inner.

我可以将策略定义更改为

type Strategy<'a> = Box<dyn Fn(&'a Vec<&'a Inner>) -> Box<dyn FnMut() -> Outer>>;

但是我得到了错误

`inners` does not live long enough
borrowed value does not live long enoughrustcE0597
main.rs(38, 1): `inners` dropped here while still borrowed
main.rs(18, 26): type annotation requires that `inners` is borrowed for `'static`

(第38行是main函数的结尾)

每次我想使用策略时,我不确定如何在不克隆整个内部结构的情况下解决这个问题。

问题在于

        move || {
            let inner = inners.choose(&mut rng).unwrap();
            if rng.gen_bool(1.0 / 2.0) {
                Outer::ActionA(inner.to_owned().clone())
            } else {
                Outer::ActionB(inner.to_owned().clone())
            }
        }

捕获inners,也就是你传入外层闭包的&Vec<&Inner>inners 不受 'static 的限制,因此闭包作为一个整体不能强制转换为 dyn FnMut() -> Outer,即 隐式 dyn (FnMut() -> Outer) + 'static

要解决此问题,您可以允许策略 return 不是 'static 的函数,方法是像这样重新定义类型:

type Strategy = Box<
    dyn for<'a>
        Fn(&'a Vec<&'a Inner>) -> Box<dyn (FnMut() -> Outer) + 'a>
>;

注意 for<'a>。这是 trait-bound 语法,等同于声明一个像

这样的函数
fn example_strategy<'a>(&'a Vec<&'a Inner>) -> Box<dyn (FnMut() -> Outer) + 'a> {...}

函数输出的生命周期取决于输入。

进行此更改并添加缺少的 mut 限定符(调用 FnMut 需要对该函数进行可变访问),您的程序将编译。

也许您希望 FnMut() -> Outer 保留 'static;在这种情况下,您需要定义 Strategy 以便它 拥有 Inner,并相应更改您对它的称呼。但是您可能不想要这个,因为您提到不克隆 Inner;我提到它是为了比较。

type Strategy = Box<dyn Fn(Vec<Inner>) -> Box<dyn FnMut() -> Outer>>;