Rust中lazy_static定义的全局HashMap能否插入非'static生命周期的User
不能向你当前定义的全局HashMap中插入非'static生命周期的User实例。
原因是lazy_static定义的全局变量本身生命周期为'static,存储在其中的所有值都必须满足'static生命周期约束,否则会出现悬垂引用的内存安全风险,这是Rust的生命周期规则明确禁止的。
你当前定义的USERS存储的是User<'static>类型,意味着User的name字段必须是存活整个程序运行周期的字符串引用。而new_user函数中的name参数是未指定生命周期的&str,默认生命周期短于'static,因此构造出的User实例生命周期不满足存入全局HashMap的要求,触发编译错误。
下面给出两种常用的可行方案,按推荐优先级排序:
方案1:使用拥有所有权的String类型(最推荐)
去掉User结构体的生命周期参数,将name字段的类型从&'a str改为String,String是拥有字符串所有权的类型,天然满足'static约束,也支持存储运行时动态生成的字符串,是日常开发的最优选择。
修改后的代码如下:
use std::collections::HashMap; use lazy_static::lazy_static; use std::sync::Mutex; // 去掉生命周期参数,使用String struct User { name: String, score: f32, } lazy_static! { static ref USERS: Mutex<HashMap<u64, User>> = Mutex::new(HashMap::new()); } fn new_user(id: u64, name: &str, score: f32) { // 把传入的&str转为String获取所有权 let user = User { name: name.to_string(), score }; USERS.lock().unwrap().insert(id, user); } fn remove_user(id: u64) { USERS.lock().unwrap().remove(&id); } fn main() { new_user(1, "hello", 1.2); remove_user(1); }
方案2:限制参数为'static引用
如果你的业务场景中name只会用到编译期确定的字符串字面量(本身就是'static生命周期),可以直接将new_user的name参数类型指定为&'static str,无需修改User结构体定义,也可正常编译。
修改后的new_user代码如下:
// 仅接受'static生命周期的字符串引用 fn new_user(id: u64, name: &'static str, score: f32) { let user = User { name, score }; USERS.lock().unwrap().insert(id, user); }
注意:该方案无法接收运行时动态生成的字符串,适用场景非常有限
不要尝试用unsafe代码绕过生命周期校验,全局变量存活于程序整个运行周期,存入其中的非'static引用一旦提前被释放,必然会触发悬垂引用,导致程序崩溃或者未知安全问题。
内容的提问来源于stack exchange,提问作者wub

