Search code examples
rustrust-cargorust-tokio

how to write values ​to arc<rwlock<hashmap<>>> out of the loop


I have a loop in which a program receives a message via a web socket and writes information about the user (from the message) to the 'database?'.

#[derive(Debug)]
struct ChatUser{
    name: String,
    password: String,
}

static NEW_USER_ID: AtomicUsize = AtomicUsize::new(1);
type UserDB = Arc<RwLock<HashMap<usize, ChatUser>>>;

async fn web_socket(mut ws: WebSocket, State(state): State<UserDB>) {
    let new_id = NEW_USER_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
    while let Ok(Message::Text(message)) = ws.next().await.unwrap(){
        let user_info: Value = serde_json::from_str(&message.trim()).unwrap();
            let (name, password) = (user_info["name"].to_string(),    user_info["password"].to_string());
            state.write().await.insert(new_id, ChatUser { name: name, password: password }).expect("cant write"); 
            println!("{:?}",state);
    }
}

state.write does not work correctly when run in a loop... outside the loop everything works correctly

Is it possible to take values ​​out of the loop? or write them down in the state in some other way?

if I run the code and send message to socket

thread 'tokio-runtime-worker' panicked at src/main.rs:41:93:
cant write
stack backtrace:
   0: rust_begin_unwind
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/std/src/panicking.rs:652:5
   1: core::panicking::panic_fmt
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/panicking.rs:72:14
   2: core::panicking::panic_display
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/panicking.rs:262:5
   3: core::option::expect_failed
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/option.rs:1995:5
   4: core::option::Option<T>::expect [............]

how can I fix it?

"can't write" <--- is the message form state.write.await.insert.expect("can't write")


Solution

  • There are 2 problem

    HashMap Insert

    The returned value of HashMap::insert() is Option<V>, which does not represent if the insertion is successful, it is always successful, however,

    • Some(V) of the old value when the key already in the HashMap, and
    • None when it is a entirely new key. I.e. when first insert a key, it will return None, you should just let _ = it .
    use std::collections::HashMap;
    fn main() {
        let mut map = HashMap::new();
        // the first time inserting a key return `None`
        assert_eq!(None, map.insert("My Key", 69));
        // subsequent insertion return the already inserted value
        assert_eq!(Some(69), map.insert("My Key", 420));
    }
    

    Break Value out of loop

    It's the wrong question but here is an answer anyway,

    You can change the while loop to a loop loop, and break a loop with a value.

    let error: std::io::Error = loop {
        // do whatever task 
        // blah blah blah
        //...
        
        // some how encounter a Result type
        let result: Result<i32, std::io::Error> = /* some error-able things */;
    
    
        let ok_i32 = match result {
            // safely unwrap the result into Ok(_)
            Ok(i) => i,
            // return the error
            Err(e) => break e,
        };
        
        // Keep doing whatever task
        drop(ok_i32);
    };