rust 即使标记了生存期,闭包也可能比当前函数更持久

zpf6vheq  于 2023-03-23  发布在  其他
关注(0)|答案(3)|浏览(97)
fn get_closeures<'a>(x: &'a i64) -> Vec<Box<dyn Fn() + 'a>> {
    let mut ret: Vec<Box<dyn Fn() -> ()>> = Vec::new();
    ret.push(Box::new(|| println!("{}", x + 1)));
    ret.push(Box::new(|| println!("{}", x + 2)));

    ret
}

fn main() {
    let x: i64 = 100;
    {
        let closures = get_closeures(&x);
        for closure in closures {
            closure();
        }
    }
}

Playground
导致错误

error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
 --> src/main.rs:3:23
  |
3 |     ret.push(Box::new(|| println!("{}", x + 1)));
  |                       ^^                - `x` is borrowed here
  |                       |
  |                       may outlive borrowed value `x`
  |
note: closure is returned here
 --> src/main.rs:6:5
  |
6 |     ret
  |     ^^^
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
  |
3 |     ret.push(Box::new(move || println!("{}", x + 1)));
  |                       ++++

我的问题是,我已经标记了存储的参数x的生存期必须长于存储到vector中的闭包。为什么编译器阻止我借用x?
我知道我可以在闭包上添加move关键字来修复它,但是如果x不是i64而是一个非Copy结构体,那么这个方法就不起作用了。
另外,我认为我的代码内部没有内存问题,如何说服Rust编译器相信它?

nukf8bse

nukf8bse1#

问题是,如果没有编译器建议的move,本地x会被引用,并且只有该引用存储在闭包中。但是x确实在get_closures的末尾被释放,因此引用现在是悬空的。要解决这个问题,你可以简单地应用编译器的建议,在每个闭包前面添加move
移动不会**复制引用后面的T,而是复制引用本身,这正是你想要的。

qc6wkl3g

qc6wkl3g2#

请注意,x不是i64类型,而是&i64类型,即 * 引用到 * i64。因此,即使底层类型不是Copyx也将始终是Copy。示例:

fn get_closeures<'a>(x: &'a str) -> Vec<Box<dyn Fn() + 'a>> {
    let mut ret: Vec<Box<dyn Fn() -> ()>> = Vec::new();
    ret.push(Box::new(move || println!("{}", x)));
    ret.push(Box::new(move || println!("{}", x)));

    ret
}

fn main() {
    let x: String = "azerty".to_string();
    {
        let closures = get_closeures(&x);
        for closure in closures {
            closure();
        }
    }
}

Playground

ct2axkht

ct2axkht3#

你可以给每个闭包分别给予&x参数:

fn get_closures() -> Vec<fn(&i64)> {
    let mut ret: Vec<fn(&i64)> = vec![];
    ret.push(|x| println!("{}", x + 1));
    ret.push(|x| println!("{}", x + 2));

    ret
}

Playground

相关问题