Rust unwrap浅析

Rust unwrap浅析
代码letdmget_data_manager().lock().unwrap();这条语句链式调用了三样东西逐层拆解链式调用拆解步骤返回值类型含义get_data_manager()LazyMutexDataManager获取全局单例的只读引用.lock()ResultMutexGuardDataManager, PoisonError...尝试获取互斥锁返回Result.unwrap()MutexGuardDataManager把Result的Ok值取出来如果是Err则直接 panic为什么lock()返回的是一个Result而不是直接给锁Mutex::lock()可能失败失败的原因是锁中毒Poisoning如果一个线程在持有锁期间panic 了异常崩溃Mutex就会被标记为中毒poisoned。之后任何线程再调用lock()都会返回Err(PoisonError)而不是Ok。这是 Rust 的一种安全机制通知后来的线程之前有人拿着锁崩了被保护的数据可能处于不一致的状态。详细例子场景 1正常情况最常见时间线 → 线程Adm DATA_MANAGER.lock() → Ok(MutexGuard) → .unwrap() → 拿到可变引用 ↓ 修改数据... ↓ 锁自动释放 线程Bdm DATA_MANAGER.lock() → Ok(MutexGuard) → .unwrap() → 拿到可变引用 ↓ 查询数据...lock() 返回值Ok(MutexGuard { data_manager }) unwrap() 结果MutexGuard { data_manager } ← 成功取出一切正常场景 2锁中毒Poisoning// 假设线程C在处理请求时持有锁然后 panic 了fnhandle_bad_request(){letdmDATA_MANAGER.lock().unwrap();// ... 处理数据过程中panic!(出了意外错误);// 线程C在这里崩溃锁没有被正常释放}// dm 的析构函数不会运行panic 时栈展开可能被中止// 之后线程D来了fnhandle_request(){letdmDATA_MANAGER.lock();// lock() 返回Err(PoisonError { guard: ... }) ← 锁被标记为中毒//// 如果使用 .unwrap()panic!(called Result::unwrap() on an Err value: PoisonError ...)// → 程序崩溃//// 如果使用 .unwrap_or_else(|e| e.into_inner())忽略中毒继续使用数据}.unwrap()在这里为什么是可接受的在这个推荐系统的场景中使用.unwrap()是合理的原因锁内操作极短只是查询哈希表几十纳秒就完成几乎不可能 panic锁内没有 panic 路径get_movie_by_id()、get_user_by_id()都是纯查询不会 panic如果真的中毒让程序崩溃反而是好事数据管理器是核心数据结构如果它处于不一致状态继续运行会返回错误数据不如立刻崩溃让监控系统发现并重启服务对比三种处理方式/方式1.unwrap()—— 中毒就崩溃当前代码的做法letdmget_data_manager().lock().unwrap();// 方式2.unwrap_or_else() —— 中毒也强行拿数据letdmget_data_manager().lock().unwrap_or_else(|poisoned|poisoned.into_inner());// 方式3match 显式处理 —— 中毒时返回错误响应letdmmatchget_data_manager().lock(){Ok(guard)guard,Err(_)returnHttpResponse::InternalServerError().json(服务内部错误),};总结概念解释lock()向 Mutex 申请锁成功返回Ok(守卫)失败中毒返回Errunwrap()取出Ok中的值如果是Err直接让当前线程panic崩溃为什么这样做数据管理器是核心中毒意味着数据可能损坏与其返回错误结果不如直接崩溃让运维介入