2017-07-03 264 views
4

我正在使用rust-websocket和基於Tokio的異步系統在Rust中編寫websocket服務器。我可以爲客戶提供服務,但是,我無法弄清楚如何在客戶端之間共享可變狀態。這裏是一些(部分)代碼演示此問題:使用async(tokio)在客戶端之間共享可變狀態rust-websocket

let mut core = Core::new().unwrap(); 
let handle = core.handle(); 
let server = Server::bind("localhost:62831", &handle).unwrap(); 

let mut state = State{ 
    ... 
}; 

let f = server.incoming() 
    .map_err(|InvalidConnection {error, ..}| error) 
    .for_each(|upgrade, _)| { 
     let f = upgrade.accept() 
      .and_then(|s, _| { 
       let ctx = ClientContext{ 
        // some other per-client values 
        state: &mut state, 
       } 
       ... 
       return s.send(Message::binary(data).into()) 
        .and_then(move |s| Ok(s, ctx)); // this could be the complete wrong way to insert context into the stream 
      }).and_then(|s, ctx| { 
       // client handling code here 
      }); 

      handle.spawn(f 
       .map_err(...) 
       .map(...) 
      ); 
      return Ok(()) 
    }); 

core.run(f).unwrap(); 

本該代碼中的錯誤:

error[E0373]: closure may outlive the current function, but it borrows `**state`, which is owned by the current function 
    --> src/main.rs:111:27 
    | 
111 |     .and_then(|(s, _)| { 
    |       ^^^^^^^^ may outlive borrowed value `**state` 
... 
114 |       state: &mut state, 
    |          ----- `**state` is borrowed here 
    | 
help: to force the closure to take ownership of `**state` (and any other referenced variables), use the `move` keyword, as shown: 
    |     .and_then(move |(s, _)| { 

當試圖編譯器的建議下,我得到這個:

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure 
    --> src/main.rs:111:27 
    | 
111 |     .and_then(move |(s, _)| { 
    |       ^^^^^^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure 

error: `state` does not live long enough 
    --> src/main.rs:114:37 
    | 
114 |       state: &mut state, 
    |          ^^^^^ does not live long enough 
... 
122 |     }) 
    |     - borrowed value only lives until here 
    | 
    = note: borrowed value must be valid for the static lifetime... 

我也嘗試在RefCell(在狀態本身之後創建RefCell)中包裝狀態,但是,編譯器會給出類似的移動錯誤,因爲它會嘗試移動RefCell到創建客戶端上下文的閉包中。

回答

1

你很接近RefCell。你現在需要的是一個Rc來包裝RefCell,所以你可以克隆Rc而不是捕獲RefCell本身。

let shared_state = Rc::new(RefCell::new(State::new()))); 
incoming().for_each(move |s, _| { 
    let shared_state = shared_state.clone(); // Left uncaptured 
    shared_state.borrow_mut().do_mutable_state_stuff(); // Could panic 
}); 

注意,因爲你正在使用Rc的和RefCell的現在,你可能需要繼續前進,你的ClientContext結構轉換爲存儲一個RC>代替&mut State的。在某些情況下可能會繼續使用&mut State,但是您的&mut State將與RefMut的生命週期相關聯,並且如果在下一次關閉運行之前保持活動狀態,則借入者會恐慌(或者如果您失敗使用try_變體)。

同時請記住,如果你決定要在你的反應堆多個線程,你只需要改變RcArc,並RefCellMutex,這是很自然的轉換在需要時。