如何将闭包泛型参数的生命周期与闭包本身的生命周期解耦?

问题描述 投票:0回答:1

我正在开发一个 Rust 项目,我希望存储一个盒装闭包的 Vec 以及一个稍后重用的通用参数。 我想修改每个存储的闭包以添加一些常见的处理,但是我期望能够使用的代码的几个版本存在问题,需要在闭包的通用参数上绑定一些

'static
,我认为这实际上并不是必需的,因为
T
的生命周期应该独立于闭包的生命周期。

所有提供的示例都可以在 rust Playground 中运行,轻松注释和取消注释相关函数以进行测试。 此外,为了清楚起见,所有示例都使用以下类型别名:

pub type MyOp<T> = dyn Fn(&mut String, &mut T);

下面的示例显示了有问题的代码:

fn test<T>() -> Vec<Box<MyOp<T>>> {
    let mut operators: Vec<Box<MyOp<T>>> = Vec::default();

    let mut add_op = |operator: Box<MyOp<T>>| {
        let operator_bis = move |s: &mut _, t: &mut _| {
            operator(s, t);
            // Do something else with s
        };
        operators.push(Box::new(operator_bis));
    };

    add_op(Box::new(|s, t| ()));
    add_op(Box::new(|s, t| ()));

    operators
}

此代码无法编译,并出现以下错误,这是我没想到的,因为

T
的生命周期不应影响最终盒装闭包的生命周期。

error[E0310]: the parameter type `T` may not live long enough
  --> src/main.rs:14:24
   |
14 |         operators.push(Box::new(operator_bis));
   |                        ^^^^^^^^^^^^^^^^^^^^^^
   |                        |
   |                        the parameter type `T` must be valid for the static lifetime...
   |                        ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
6  | fn test<T: 'static>() -> Vec<Box<MyOp<T>>> {
   |          +++++++++

有趣的是,不调用运算符闭包就可以编译,但显然代码并没有达到我最初的目标。

fn test<T>() -> Vec<Box<MyOp<T>>> {
    let mut operators: Vec<Box<MyOp<T>>> = Vec::default();

    let mut add_op = |operator: Box<MyOp<T>>| {
        operators.push(operator);
    };

    add_op(Box::new(|_s, _rng| ()));
    add_op(Box::new(|_s, _rng| ()));

    operators
}

下面的示例显示了依赖辅助函数来修改闭包的工作版本。 该版本的优点是可以接受盒装和非盒装封口。

fn add_operator<T>(
    operator: impl Fn(&mut String, &mut T) + 'static,
    operators: &mut Vec<Box<MyOp<T>>>,
) {
    let op = move |s: &mut _, t: &mut _| {
        operator(s, t);
        // Do something else with s
    };
    operators.push(Box::new(op));
}

fn test<T>() -> Vec<Box<MyOp<T>>> {
    let mut operators = Vec::default();
    add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
    add_operator(|s, t| (), &mut operators);
    operators
}

但是,我也希望下面的版本只接受闭包进行编译:

fn add_operator<T>(
    operator: Box<MyOp<T>>,
    operators: &mut Vec<Box<MyOp<T>>>,
) {
    let op = move |s: &mut _ , t: &mut _| {
        operator(s, t);
        // Do something else with s
    };
    operators.push(Box::new(op));
}

fn test<T>() -> Vec<Box<MyOp<T>>> {
    let mut operators = Vec::default();
    add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
    add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
    operators
}

我认为这可能与 Rust 存储库上的 this issues 有关,因为编译器无法检测到

T
的生命周期并不真正相关。 但是,我对与闭包相关的生命周期问题的理解并没有那么有信心,在提出问题之前,我想知道是否有人可以查明我的代码中的错误,或者是否所有版本都应该能够编译。

以下编辑后添加信息。


由于到目前为止收到的评论和答案都解释了如何编译示例,考虑到

'static
类型别名上隐藏的
dyn
绑定,我试图想出一个很好的例子来表明我想要
T
生命周期独立于函数的生命周期。

目标是能够执行以下示例中的操作,其中该函数应该能够使用任何

Cow
进行调用,但实际上失败了,即使使用我最初认为有效的版本也是如此。 在这种情况下,
a
的生命周期即使它不应该发挥作用,因为在调用操作员之后它不会被使用。

fn main() {
    let operators_cow = test::<Cow<'_, [u32]>>();

    let mut s = String::new();
    let a = [10u32, 20];
    let mut t = Cow::from(a.as_slice());
    operators_cow[0](&mut s, &mut t);

    drop(operators_cow); // Does not compile if uncommented 
}
generics rust closures lifetime
1个回答
0
投票

正如 cafce25 指出的,

pub type MyOp<T> = dyn Fn(&mut String, &mut T);
相当于
pub type MyOp<T> = dyn Fn(&mut String, &mut T) + 'static;
。您需要为类型别名添加生命周期,并且还需要使用它的函数。

如果你正确地用生命周期注释你的代码,它就会编译:

pub type MyOp<'a, T> = dyn Fn(&mut String, &mut T) + 'a;

fn add_operator<'a, T: 'a>(
    operator: Box<MyOp<'a, T>>,
    operators: &mut Vec<Box<MyOp<'a, T>>>,
) {
    let op = move |s: &mut _ , t: &mut _| {
        operator(s, t);
        // Do something else with s
    };
    operators.push(Box::new(op));
}

fn test<'a, T: 'a>() -> Vec<Box<MyOp<'a, T>>> {
    let mut operators = Vec::default();
    add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
    add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
    operators
}
© www.soinside.com 2019 - 2024. All rights reserved.