[英]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 全面禁止返回對從閉包中捕獲的數據的任何引用。
據我所知,解決方案是:
不要使用閉包,而是將groups
作為參數傳遞給 function(匿名或命名)
使用某種共享所有權和內部可變性,例如存儲 map 並返回Rc<RefCell<String>>
使用方法將閉包脫糖到結構中,這樣生命周期就變得易於管理:
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.