Как записать значения в arc<rwlock<hashmap<>>> вне цикла

У меня есть цикл, в котором программа получает сообщение через веб-сокет и записывает информацию о пользователе (из сообщения) в «базу данных?».

#[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 работает неправильно при запуске в цикле... вне цикла все работает правильно

Можно ли взять значения из цикла? или записать их в штат каким-то другим способом?

если я запущу код и отправлю сообщение в сокет

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 [............]

как я могу это исправить?

«не могу написать» <--- это форма сообщения state.write.await.insert.expect («не могу написать»)

HashMap::insert вернет предыдущее значение, если оно существовало, поэтому ваш .expect() запаникует, если значение еще не существует, а это, я думаю, не то, что вы хотели (в конце концов, это new_id). Просто уберите .expect().
kmdreko 03.09.2024 05:46

Insert записывает новое значение внутрь, не так ли?

kecakisa 03.09.2024 06:29

Да, но возвращаемое значение — это Option<V>, Some(V) старого значения, если ключ уже находится в HashMap, и None, когда это совершенно новый ключ. Т.е. когда вы впервые вставите ключ, он вернется None, вам нужно просто let _ = его .

啊鹿Dizzyi 03.09.2024 07:48

Сообщение в ожидании показывает природу путаницы. Если бы state.write() возвращал Result, то expect("can't write") имело бы смысл и было бы необходимо, но оно было бы в другом месте: state.write().await.expect("can't write").insert(...). Но это не так, и ваш expect() помещается в заявку на Option, возвращенный HashMap::insert(). Сообщение, отражающее то, что он делает, будет чем-то вроде expect("didn't overwrite previous value"). Поскольку вы не хотите утверждать, что перезаписали предыдущее значение, expect() можно удалить.

user4815162342 03.09.2024 17:02

Я дал это сообщение, чтобы понять, какая строка кода вызывает ошибку, никакого особого смысла сообщению я не придал

kecakisa 04.09.2024 03:33
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
6
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Есть 2 проблемы

Вставка HashMap

Возвращаемое значение HashMap::insert() равно Option<V>, что не означает, что вставка прошла успешно, однако она всегда успешна.

  • Some(V) старого значения, когда ключ уже есть в HashMap, и
  • None когда это совершенно новый ключ. Т.е. когда вы впервые вставляете ключ, он возвращает None, вам нужно просто let _ = его.
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));
}

Вырвать значение из цикла

Это неправильный вопрос, но в любом случае вот ответ,

Вы можете заменить цикл while на цикл loop и разорвать цикл со значением.

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);
};
let _ = ... является ненужным и унидиоматическим для HashMap::insert(). Option — это не Result, и его можно просто выбросить. Другими словами, вместо let _ = map.insert(...), как предложено в ответе, можно просто использовать map.insert(...).
user4815162342 03.09.2024 16:57

Другие вопросы по теме