[英]How do I mutate a variable from inside a rust closure
我正在嘗試在 rust 中實現類似EventListener
的接口。 我有一個需要回調的特征,回調應該從定義它的 scope 中改變一個變量。但是我收到一個錯誤,說借來的值不夠長。
pub struct Target<T> {
funcs: Vec<Box<dyn FnMut(T) -> ()>>,
}
impl<T: Clone + 'static> Target<T> {
pub fn new() -> Target<T> {
return Target { funcs: Vec::new() };
}
pub fn add_listener(&mut self, func: Box<dyn FnMut(T) -> ()>) -> () {
self.funcs.push(Box::new(func));
}
pub fn trigger(&mut self, x: T) {
for callback in &mut self.funcs {
callback(x.clone());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trigger_mutation() {
let mut a = 0;
let mut t: Target<i32> = Target::new();
t.add_listener(Box::new(|x: i32| {
println!("{}", x);
a = x;
}));
t.trigger(42);
let b = a.clone();
assert_eq!(b, 42);
}
}
我運行它並得到這個:
$ cargo test -- --nocapture
Compiling luma-connector v0.1.0 (/home/blake/git/connector)
error[E0597]: `a` does not live long enough
--> src/target.rs:32:13
|
30 | t.add_listener(Box::new(|x: i32| {
| - -------- value captured here
| ________________________|
| |
31 | | println!("{}", x);
32 | | a = x + 1;
| | ^ borrowed value does not live long enough
33 | | }));
| |__________- cast requires that `a` is borrowed for `'static`
...
37 | }
| - `a` dropped here while still borrowed
但我得到一個錯誤,說借來的價值不夠長。
好吧,是的,您鍵入Target
與范圍無關,因此就 Rust 類型系統而言,閉包可能會飛入不受時間限制的空間(例如add_listener
可以將其傳遞給單獨的線程),因此比trigger_mutation
壽命長,這意味着a
壽命不夠長。
有兩種方法可以解決此問題:
使用具有內部可變性(分別為Mutex
和RefCell
)的Arc
/ Rc
以放寬a
的生命周期: Arc version [0], Rc version ,這可能是最簡單的,並且對Target
限制最少,盡管它來自運行時成本。
或者,您可以“范圍” Target
告訴 Rust 它不會逃脫,因此一切都是完美的。 我不確定這是否是最好的方法(希望其他人可以提供該信息),但是在一生中限制FnMut
將允許 rustc 對此進行推理: https://play.rust-lang.org/?version=stable&mode =debug&edition=2018&gist=e67a4ab0faa8cc5d01c75293623c9fb4
這在運行時基本上是免費的,但這意味着Target
無法真正逃脫它的 function。
所以前者可能是你想要的,后者似乎對事件/通知系統不是超級有用,但是 YMMV。
[0] Atomic*
也適用於Arc
版本,並且比互斥鎖更容易且更便宜,盡管它可能與測試用例不太相關。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.