簡體   English   中英

返回對捕獲的可變變量的引用

[英]Returning a reference to a captured mutable variable

所以我有一個 function 看起來像這樣

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

編輯

正如@Sven Marnach指出的那樣,這里的問題是我可以為同一個 object 創建 2 個可變引用:

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

原文(錯誤)

我認為 Rust 告訴我,閉包group正在可變地捕獲變量groups ,然后返回對 object 擁有的引用groups 所以這里的危險是下面的代碼會返回一個懸空指針(因為在foo完成后它從 scope 中消失時groups被丟棄)。

  fn foo() -> &String { /*... */ 返回組(0.1);  }

那么有沒有辦法像這樣從捕獲的可變HashMap返回引用?

我認為 Rust 告訴我,閉包組可變地捕獲變量組,然后返回對組擁有的 object 的引用。 所以這里的危險是,如果我要寫:

然后我將返回一個懸空指針(因為在 foo 完成后它從 scope 中消失時組被丟棄)。

不會。如果是這種情況,Rust 可以(並且會)對此發出警告

問題是 function 特征周圍的生命周期是有問題的,因為它們沒有辦法將結果的生命周期與 function 本身的生命周期相匹配。

因此,rust 全面禁止返回對從閉包中捕獲的數據的任何引用

據我所知,解決方案是:

  1. 不要使用閉包,而是將groups作為參數傳遞給 function(匿名或命名)

  2. 使用某種共享所有權和內部可變性,例如存儲 map 並返回Rc<RefCell<String>>

  3. 使用方法將閉包脫糖到結構中,這樣生命周期就變得易於管理:

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 完全移動到包裝器中(並且結構可以完全初始化自身而無需兩步初始化):

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM