将 &mut 传递给函数并返回闭包的生命周期问题

Lifetime problems passing &mut to function and returning a closure

我有一个通过分而治之递增向量的简短示例。非常基本,我只是无法正确获得生命周期。我很确定它与 &'s mut 参数生命周期和 TaskResult<'s> return 生命周期有关,但我不确定如何让它工作。

code on the playpen

fn main() {
    let mut data = vec![1,2,3,4,5,6,7,8,9];
    let t = inc_vec(data.as_mut_slice());
}

pub type MyClosure<'s> = FnMut() -> TaskResult<'s> + Send + 's;

pub enum TaskResult<'s> {
    Done(usize),
    Fork(Vec<Box<MyClosure<'s>>>),
}

fn inc_vec<'s>(data: &'s mut [usize]) -> TaskResult {
    if data.len() <= 4 {
        inc_vec_direct(data)
    } else {
        inc_vec_fork(data)
    }
}

fn inc_vec_fork<'s>(data: &'s mut [usize]) -> TaskResult<'s> {
    let mid = data.len()/2;
    let (l,r) = data.split_at_mut(mid);

    let task_l: Box<MyClosure<'s>> = Box::new(move || {
        inc_vec(l)
    });
    let task_r: Box<MyClosure<'s>> = Box::new(move || {
        inc_vec(r)
    });

    TaskResult::Fork(vec![task_l, task_r])
}

fn inc_vec_direct(data: &mut [usize]) -> TaskResult {
    for d in data {
        *d += 1;
    }
    TaskResult::Done(1)
}

它给了我以下错误(由于相同的错误产生了两次而被截断,一次是 task_l,一次是 task_r):

src/main.rs:26:17: 26:18 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
src/main.rs:26         inc_vec(l)
                               ^
src/main.rs:25:55: 27:6 note: first, the lifetime cannot outlive the lifetime  as defined on the block at 25:54...
src/main.rs:25     let task_l: Box<MyClosure<'s>> = Box::new(move || {
src/main.rs:26         inc_vec(l)
src/main.rs:27     });
src/main.rs:26:17: 26:18 note: ...so that closure can access `l`
src/main.rs:26         inc_vec(l)
                               ^
src/main.rs:21:62: 33:2 note: but, the lifetime must be valid for the lifetime 's as defined on the block at 21:61...
src/main.rs:21 fn inc_vec_fork<'s>(data: &'s mut [usize]) -> TaskResult<'s> {
src/main.rs:22     let mid = data.len()/2;
src/main.rs:23     let (l,r) = data.split_at_mut(mid);
src/main.rs:24
src/main.rs:25     let task_l: Box<MyClosure<'s>> = Box::new(move || {
src/main.rs:26         inc_vec(l)
               ...
src/main.rs:25:38: 27:7 note: ...so that trait type parameters matches those specified on the impl (expected `TaskResult<'_>`, found `TaskResult<'s>`)
src/main.rs:25     let task_l: Box<MyClosure<'s>> = Box::new(move || {
src/main.rs:26         inc_vec(l)
src/main.rs:27     });

必须有一个简单的解决方法。我想说的是,我 return 一个闭包向量,它具有对输入切片部分的可变引用。我想我必须将闭包生命周期标记为比数据切片生命周期短,只是不知道该怎么做。

你可以编译你的例子,运行 如果你改变一行:

pub type MyClosure<'s> = FnOnce() -> TaskResult<'s> + Send + 's;
//                       ^~~~~~

不过我还在想怎么解释呢!

这是我开始的代码。我做了一些简化来开始,主要是围绕删除不需要的生命周期引用。生命周期省略意味着 fn(foo: &T) -> &Ufn<'a>(foo: &'a T) -> &'a U 相同,但 fn<'a>(foo: &'a T) -> &U 不同

fn main() {
    let mut data = vec![1,2,3,4,5,6,7,8,9];
    let t = inc_vec(data.as_mut_slice());
}

pub type MyClosure<'s> = FnMut() -> TaskResult<'s> + Send + 's;

pub enum TaskResult<'s> {
    Done(usize),
    Fork(Vec<Box<MyClosure<'s>>>),
}

fn inc_vec(data: &mut [usize]) -> TaskResult {
    if data.len() <= 4 {
        inc_vec_direct(data)
    } else {
        inc_vec_fork(data)
    }
}

fn inc_vec_fork(data: &mut [usize]) -> TaskResult {
    let mid = data.len() / 2;
    let (l, r) = data.split_at_mut(mid);

    let task_l: Box<MyClosure> = Box::new(move || inc_vec(l));
    let task_r: Box<MyClosure> = Box::new(move || inc_vec(r));

    TaskResult::Fork(vec![task_l, task_r])
}

fn inc_vec_direct(data: &mut [usize]) -> TaskResult {
    for d in data { *d += 1; }
    TaskResult::Done(1)
}

大多数情况下,我只是稍微改变一下闭包就得到了结果:

let task_l: Box<MyClosure> = Box::new(move || { let a = l; inc_vec(a)});

这应该是相同的代码。但是,这有错误:

error: cannot move out of captured outer variable in an `FnMut` closure
     let task_l: Box<MyClosure> = Box::new(move || { let a = l; inc_vec(a)});
                                                             ^
note: attempting to move value to here
     let task_l: Box<MyClosure> = Box::new(move || { let a = l; inc_vec(a)});
                                                         ^
help: to prevent the move, use `ref a` or `ref mut a` to capture value by reference

这让我尝试了各种 Fn* 特征,FnOnce 有效。我 认为 解决方案归结为 Rust 不允许可变引用的别名(a.k.a。你不能指向同一个可变的东西两次)。如果您有 FnMutFn,那么您可以多次调用闭包,这将提供创建别名的机会。不过,如果错误消息包含有关可变性的任何信息,那就太好了!

A FnOnce 保证只被调用一次,这防止了特定的别名机会。

我认为您可以从中提交 1 或 2 个错误:

  1. 令人惊讶的是,错误消息会根据是否存在 let 而改变。
  2. 最好提及可变性作为变量不能移入闭包的原因。