【问题标题】:Anonymous function - cannot infer an appropriate lifetime due to conflicting requirements匿名函数 - 由于需求冲突,无法推断出合适的生命周期
【发布时间】:2021-11-02 15:51:42
【问题描述】:

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

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函数的结尾)

每次我想使用策略时,如果不克隆整个内部,我不确定如何解决这个问题。

【问题讨论】:

    标签: rust lifetime ownership


    【解决方案1】:

    问题是

            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,这是您传入外部闭包的&amp;Vec&lt;&amp;Inner&gt;inners 不受'static 的限制,因此不能将整个闭包强制为dyn FnMut() -&gt; Outer,即隐式 dyn (FnMut() -&gt; Outer) + 'static

    要解决此问题,您可以允许策略返回不是'static 的函数,方法是重新定义类型,如下所示:

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

    注意for&lt;'a&gt;。这是 trait-bound 语法等价于声明一个函数,如

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

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

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

    也许您希望FnMut() -&gt; Outer 保留'static;在这种情况下,您需要定义 Strategy 以便它拥有 Inners,并相应地更改您的调用方式。但是你可能不想要这个,因为你提到不克隆Inners;我提一下是为了比较。

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

    【讨论】:

    • 正是我需要的,谢谢!为什么mut 必须最后添加到let mut get_choice...?是因为它是 FnMut 还是与增加的生命周期有关?
    • @user82395214 是的,调用FnMut 需要对函数进行可变访问,仅此而已。
    猜你喜欢
    • 2017-05-07
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 2020-01-29
    • 2022-01-06
    • 2017-07-12
    • 1970-01-01
    相关资源
    最近更新 更多