简体   繁体   English

终身问题传递&mut到函数并返回一个闭包

[英]Lifetime problems passing &mut to function and returning a closure

I have a short example of incrementing a vector by divide and conquer. 我有一个简短的例子,通过除法和征服递增一个向量。 Very basic, I just can't get the lifetimes correct. 非常基本,我只是不能让生命正确。 I'm pretty sure it has to do with &'s mut argument lifetime and the TaskResult<'s> return lifetime, but I'm not sure how to make it work. 我很确定它与&'s mut参数生命周期和TaskResult<'s>返回生命周期有关,但我不确定如何使其工作。

The 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)
}

And it gives me the following error (truncated since the same error is produced twice, once for task_l and once for task_r ): 它给了我以下错误(截断,因为相同的错误产生两次,一次为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     });

There must be a simple fix to this. 必须有一个简单的解决方案。 All I want to say is that I return a vector of closures that have mutable references to parts of the input slice. 我想说的是,我返回一个闭包向量,它对输入切片的某些部分有可变引用。 I guess I have to mark the closure lifetime as shorter than the data slice lifetime, just not sure how to do that. 我想我必须将闭包生命周期标记为比数据切片生命周期短,只是不确定如何做到这一点。

You can get your example to compile and run if you change one line: 如果更改一行,可以编译并运行示例:

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

I'm still thinking through how to explain it though! 我还在考虑如何解释它!

This is the code I was starting from. 这是我开始的代码。 I made a few simplifications to get started, mostly around removing the lifetime references where they aren't needed. 我做了一些简化来开始,主要是删除不需要它们的生命周期引用。 Lifetime elision means that fn(foo: &T) -> &U is the same as fn<'a>(foo: &'a T) -> &'a U , but not the same as fn<'a>(foo: &'a T) -> &U . 终身省略意味着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)
}

Mostly, I got to the result by changing the closure just a smidge: 大多数情况下,我通过改变闭包来达到结果:

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

Which should be the same code. 哪个应该是相同的代码。 However, this has the error: 但是,这有错误:

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

Which led me to try the various Fn* traits, with FnOnce working. 这导致我尝试各种Fn*特性, FnOnce工作。 I think that the solution boils down to the fact that Rust disallows aliasing of mutable references (aka you can't point to the same mutable thing twice). 认为解决方案归结为Rust不允许使用可变引用的别名(也就是说你不能指向两次相同的可变事物)。 If you had a FnMut or Fn , then you could call the closure multiple times, which would give an opportunity to create aliases. 如果你有一个FnMutFn ,那么你可以多次调用闭包,这将有机会创建别名。 It'd be awful nice if the error message included anything about mutability though! 如果错误消息包含有关可变性的任何内容,那将是非常好的!

A FnOnce is guaranteed to only be called once, which prevents that particular aliasing opportunity. 保证FnOnce只被调用一次,这可以防止特定的混叠机会。

I think you could file 1 or 2 bugs from this: 我想你可以提出1或2个错误:

  1. It's surprising that the error message changes based on if there is a let or not. 这是令人惊讶的是基于错误信息的变化,如果有一个let与否。
  2. It would be nice to mention mutability as the reason that the variable can't be moved into the closure. 提及可变性作为变量无法移入闭包的原因会很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM