簡體   English   中英

Rust 在同一個全局 scope 內關閉的生命周期問題

[英]Rust lifetime issue with closures within same global scope

這是我的代碼中問題的最小表示:

use std::collections::HashMap;
fn main() {
  let mut map : HashMap<String, String> = HashMap::new();
  map.insert(String::from("a"), String::from("first"));
  map.insert(String::from("b"), String::from("second"));

  let mut funcs: Vec<Box<dyn FnMut(String) -> ()>> = Vec::new(); 
  for (key, val) in map { 
    funcs.push(Box::new(|v| {
      println!("{} : {} : {}",key,val,v);
    }))
  }

  for mut func in funcs {
    func(String::from("c"));
  }
}

錯誤:(與val相同):

`key` does not live long enough
values in a scope are dropped in the opposite order they are defined rustcE0597
main.rs(12, 5): `key` dropped here while still borrowed
main.rs(9, 29): value captured here
main.rs(17, 1): borrow might be used here, when `funcs` is dropped and runs the 
`Drop` code for type `Vec`

這是我真實生活用例的極簡主義表示——我正在使用一個庫,它需要我為它提供一堆閉包以便稍后組合執行。 我首先構建一個 HashMap,然后遍歷它以創建 FnMut 列表(在這個最小示例中,Fn 也可以)

我得到的錯誤的一般想法是我正在創建具有臨時生命周期的key (和val ),該生命周期在每個循環結束時結束,並且閉包具有“延遲”執行,他們將在其中訪問此數據,而不會不再存在 - 但我不知道如何修復它。 我嘗試map.into_iter()map.drain() (不使用引用),用Rc包裝數據 - 無濟於事。 這里的正確解決方案是什么?

由於構建 map 是一個有點復雜的過程,如果可能的話,我仍然希望在關閉之前構建它。

編輯1:

@Masklinn 在閉包前添加move的解決方案在這里確實有效,但它在我的真實代碼中產生了其他問題

  • 我使用的 map 的值是Vec<MyStruct> ,它不可復制,因此無法移動。 我想我可以通過實現復制特征來解決它(我的結構足夠小,基本上是一堆選項)
  • 第二個問題是,當我創建多個閉包時,我在多個閉包(非可變)中借用了第二個 HashMap,因此在第二次閉包之后我得到use of moved value錯誤:如何處理這種情況?

EDIT2:使用move時第二個問題的說明:

fn main() {
let mut map : HashMap<String, Vec<String>> = HashMap::new();
map.insert(String::from("a"), Vec::from([String::from("first")]));
map.insert(String::from("b"), Vec::from([String::from("first")]));

let map2 : HashMap<String, Vec<String>> = HashMap::new();

let mut funcs: Vec<Box<dyn FnMut(String) -> ()>> = Vec::new(); 
for (key, val) in map { 
    funcs.push(Box::new(move |v| {
        println!("{} : {:?} : {}, {}",key,val,v, map2.capacity());
    }))
}

for mut func in funcs {
    func(String::from("c"));
}

}

錯誤:

use of moved value: `map2`
value moved into closure here, in previous iteration of looprustcE0382
main.rs(12, 54): use occurs due to use in closure
main.rs(7, 9): move occurs because `map2` has type `HashMap<String, Vec<String>>`, which does not implement the `Copy` trait

我應該只使用可復制數據(如果是,如何?將 hashmap 包裝到自定義可復制結構中?),還是有其他方法?

編輯:解決方案

感謝@Masklinn:將不可復制的數據包裝到 Rc 中非常有效!

use std::{collections::HashMap, rc::Rc};
fn main() {
    let mut map : HashMap<String, Vec<String>> = HashMap::new();
    map.insert(String::from("a"), Vec::from([String::from("first")]));
    map.insert(String::from("b"), Vec::from([String::from("first")]));

    let map2 : HashMap<String, Vec<String>> = HashMap::new();
    let rc_map2: Rc<HashMap<String, Vec<String>>> = Rc::new(map2);

    let mut funcs: Vec<Box<dyn FnMut(String) -> ()>> = Vec::new(); 
    for (key, val) in map {
        let rc_map2_clone = rc_map2.clone();
        funcs.push(Box::new(move |v| {
            println!("{} : {:?} : {}, {}",key,val,v, rc_map2_clone.capacity());
        }))
    }

    for mut func in funcs {
        func(String::from("c"));
    }
}

這里的正確解決方案是什么?

只需在閉包前添加關鍵字move即可:

    funcs.push(Box::new(move |v| {
      println!("{} : {} : {}",key,val,v);
    }))

默認情況下,lambda 嘗試推斷應該如何捕獲關閉的變量(引用、可變引用或值)。 然而,這完全基於使用情況

這里你只是打印鍵和值,所以使用要求只是共享引用,因此在閉包中你有key: &Stringval: &String

通過使用move閉包,您告訴 lambda 始終按值捕獲(並且您將管理捕獲,例如按值引用)。

通過在這里使用move閉包,我們讓閉包擁有keyvalue String s,因此它們的生命周期不再是問題。

暫無
暫無

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

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