簡體   English   中英

如何從 rust 閉包內部改變變量

[英]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壽命不夠長。

有兩種方法可以解決此問題:

使用具有內部可變性(分別為MutexRefCell )的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.

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