所以我有一个函数,看起来像这样

fn foo() {
    let items = vec![0.2, 1.5, 0.22, 0.8, 0.7, 2.1];
    let mut groups: HashMap<u32, String> = HashMap::new();

    let mut group = |idx: f32| -> &mut String {
        let rounded = (idx / 0.2).floor() as u32;
        groups
            .entry(rounded)
            .or_insert_with(|| format!("{}:", rounded))
    };

    for item in items.iter() {
        group(*item).push_str(&format!(" {}", item))
    }
}

此代码不编译,出现以下错误:

error: captured variable cannot escape `FnMut` closure body
  --> src/main.rs:9:9
   |
5  |       let mut groups: HashMap<u32, String> = HashMap::new();
   |           ---------- variable defined here
6  | 
7  |       let mut group = |idx: f32| -> &mut String {
   |                                     - inferred to be a `FnMut` closure
8  |           let rounded = (idx / 0.2).floor() as u32;
9  |           groups
   |           ^-----
   |           |
   |  _________variable captured here
   | |
10 | |             .entry(rounded)
11 | |             .or_insert_with(|| format!("{}:", rounded))
   | |_______________________________________________________^ returns a reference to a captured variable which escapes the closure body
   |
   = note: `FnMut` closures only have access to their captured variables while they are executing...
   = note: ...therefore, they cannot allow references to captured variables to escape

Edit

正如@Sven Marnach所指出的,这里的问题是,我可以为同一个对象创建两个可变引用:

fn foo() {
    // ...    
    let ok = groups(0.1);
    let problem = groups(0.1);
}

Original (incorrect)

我认为Rust告诉我,闭包group可变地捕获变量groups,然后返回对groups拥有的对象的引用.所以这里的危险在于,下面的代码将返回一个悬空指针(因为groupsfoo完成后超出范围时会被删除).

fn foo() -> &String {
        /* ... */ return groups(0.1); }

那么,有没有办法像这样从捕获的可变HashMap返回一个引用?

推荐答案

我认为Rust告诉我,闭包组可变地捕获变量组,然后返回对组拥有的对象的引用.所以这里的危险是,如果我写:

然后我将返回一个悬空指针(因为在foo完成后,当它超出范围时,组将被删除).

不.如果是这样的话,铁 rust 可能(而且会)警告大约that人.

问题在于,功能特征的生存期是有问题的,因为它们无法将结果的生存期与the function itself的生存期相匹配.

因此,铁 rust 毯禁止关闭后返回any reference to captured data个.

据我所知,解决方案是:

  1. 不要使用闭包,而是将groups作为参数传入函数(匿名或命名)

  2. 使用某种共享所有权和内部易变性,例如 map 存储并返回Rc<RefCell<String>>

  3. 用一种方法将封闭物与 struct 分离,从而使使用生命周期 变得易于管理:

use std::collections::HashMap;

struct F<'a>(&'a mut HashMap<u32, String>);

impl F<'_> {
    fn call(&mut self, idx: f32) -> &mut String {
        let rounded = (idx / 0.2).floor() as u32;
        self.0
            .entry(rounded)
            .or_insert_with(|| format!("{}:", rounded))
    }
}

pub fn foo() {
    let items = vec![0.2, 1.5, 0.22, 0.8, 0.7, 2.1];
    let mut groups: HashMap<u32, String> = HashMap::new();

    let mut group = F(&mut groups);

    for item in items.iter() {
        group.call(*item).push_str(&format!(" {}", item))
    }
}

请注意,上面存储了一个与原始闭包匹配的引用,但实际上我认为没有理由不将hashmap完全移动到包装器中(并且 struct 可以完全初始化自己,而不需要两步初始化):

use std::collections::HashMap;

struct F(HashMap<u32, String>);

impl F {
    fn new() -> Self { Self(HashMap::new()) }
    fn call(&mut self, idx: f32) -> &mut String {
        let rounded = (idx / 0.2).floor() as u32;
        self.0
            .entry(rounded)
            .or_insert_with(|| format!("{}:", rounded))
    }
}

pub fn foo() {
    let items = vec![0.2, 1.5, 0.22, 0.8, 0.7, 2.1];

    let mut group = F::new();

    for item in items.iter() {
        group.call(*item).push_str(&format!(" {}", item))
    }
}

Rust相关问答推荐

使用Rust s serde_json对混合数据类型进行优化'

在Rust中赋值变量有运行时开销吗?

具有对同一类型的另一个实例的可变引用的

为什么允许我们将可变引用转换为不可变引用?

如何点击()迭代器?

函数内模块的父作用域的访问类型

取得本地对象字段的所有权

我们能确定Rust会优化掉Clone()吗?如果它会立即掉落?

在 Rust 中,在需要引用 self 的 struct 体方法中使用闭包作为 while 循环条件

为什么将易错函数的泛型结果作为泛型参数传递 infer ()?不应该是暧昧的吗?

如何将一个矩阵的列分配给另一个矩阵,纳尔代数?

如何为已实现其他相关 std trait 的每个类型实现一个 std Trait

如何从 rust 中的同一父目录导入文件

Rust 中的自动取消引用是如何工作的?

Rust 中 Mutex<> 的深拷贝?

有没有办法通过命令获取 Rust crate 的可安装版本列表?

如何在 Rust 中将 bson::Bson 转换为 Vec

如何为返回正确类型的枚举实现 get 方法?

火箭整流罩、tokio-scheduler 和 cron 的生命周期问题

在派生宏中使用枚举字段