单线程Rust环境下如何共享使用BTreeMap变量?
Rust中用全局变量替代多函数局部变量的实现方案
问题背景
想在单线程工具中用全局变量替代不同函数里的局部BTreeMap,尝试过全局变量和函数传参,但新手难以处理生命周期和类型转换问题,不需要互斥锁。
尝试1:直接定义全局变量失败
use std::collections::BTreeMap; // 尝试但失败 // let mut guess_number = BTreeMap::new(); // | ^^^ expected item fn read_csv() { let mut guess_number = BTreeMap::new(); let lines = ["Tom,4", "John,6"]; for line in lines.iter() { let split = line.split(","); let vec: Vec<_> = split.collect(); println!("{} {:?}", line, vec); let number: u16 = vec[1].trim().parse().unwrap(); guess_number.insert(vec[0], number); } for (k, v) in guess_number { println!("{} {:?}", k, v); } } fn main() { let mut guess_number = BTreeMap::new(); guess_number.insert("Tom", 3); guess_number.insert("John", 7); if guess_number.contains_key("John") { println!("John's number={:?}", guess_number.get("John").unwrap()); } read_csv(); }
错误原因:Rust全局上下文仅允许静态项(static),普通let声明不合法。
尝试2:函数传参但类型缺失
use std::collections::BTreeMap; fn read_csv(guess_number: BTreeMap) { // ^^^^^^^^ expected 2 generic arguments let lines = ["Tom,4", "John,6"]; for line in lines.iter() { let split = line.split(","); let vec: Vec<_> = split.collect(); println!("{} {:?}", line, vec); let number: u16 = vec[1].trim().parse().unwrap(); guess_number.insert(vec[0], number); } } fn main() { let mut guess_number = BTreeMap::new(); guess_number.insert("Tom", 3); guess_number.insert("John", 7); if guess_number.contains_key("John") { println!("John's number={:?}", guess_number.get("John").unwrap()); } read_csv(guess_number); for (k, v) in guess_number { println!("{} {:?}", k, v); } }
错误原因:BTreeMap是泛型类型,必须指定键值类型;直接传值会转移所有权,无法在main中继续使用原变量。
尝试3:指定类型但引用不可变
use std::collections::BTreeMap; fn read_csv(guess_number: &BTreeMap<&str, i32>) { let lines = ["Tom,4", "John,6"]; for line in lines.iter() { let split = line.split(","); let vec: Vec<_> = split.collect(); println!("{} {:?}", line, vec); let number: i32 = vec[1].trim().parse().unwrap(); guess_number.insert(vec[0], number); } for (k, v) in guess_number { println!("{} {:?}", k, v); } } fn main() { let mut guess_number: BTreeMap<&str, i32> = BTreeMap::new(); guess_number.insert("Tom", 3); guess_number.insert("John", 7); if guess_number.contains_key("John") { println!("John's number={:?}", guess_number.get("John").unwrap()); } read_csv(&guess_number); for (k, v) in guess_number { println!("{} {:?}", k, v); } }
错误信息:
7 | fn read_csv(guess_number: &BTreeMap<&str, i32>) { | -------------------- help: consider changing this to be a mutable reference: `&mut BTreeMap<&str, i32>` ... 16 | guess_number.insert(vec[0], number); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `guess_number` is a `&` reference, so the data it refers to cannot be borrowed as mutable
错误原因:不可变引用(&)无法修改指向的数据,必须使用可变引用(&mut)。
推荐方案:使用可变引用(Rust官方推荐)
这是最符合Rust所有权模型的方案,避免全局变量的副作用:
use std::collections::BTreeMap; fn read_csv(guess_number: &mut BTreeMap<&str, i32>) { let lines = ["Tom,4", "John,6"]; for line in lines.iter() { let split = line.split(","); let vec: Vec<_> = split.collect(); println!("{} {:?}", line, vec); let number: i32 = vec[1].trim().parse().unwrap(); guess_number.insert(vec[0], number); } } fn main() { let mut guess_number: BTreeMap<&str, i32> = BTreeMap::new(); guess_number.insert("Tom", 3); guess_number.insert("John", 7); if guess_number.contains_key("John") { println!("John's number={:?}", guess_number.get("John").unwrap()); } read_csv(&mut guess_number); for (k, v) in guess_number { println!("{} {:?}", k, v); } }
核心要点:
- 函数参数用
&mut BTreeMap<&str, i32>表示可变引用,允许修改原变量 - 调用时传递
&mut guess_number,临时转移可变借用权 - 符合Rust借用规则,编译时保证内存安全
全局变量实现方案(不推荐,但满足需求)
如果一定要用全局变量,单线程下可通过lazy_static+RefCell实现:
首先在Cargo.toml添加依赖:
[dependencies] lazy_static = "1.4.0"
代码实现:
use std::collections::BTreeMap; use std::cell::RefCell; use lazy_static::lazy_static; lazy_static! { static ref GUESS_NUMBER: RefCell<BTreeMap<&'static str, i32>> = RefCell::new(BTreeMap::new()); } fn read_csv() { let mut map = GUESS_NUMBER.borrow_mut(); let lines = ["Tom,4", "John,6"]; for line in lines.iter() { let split = line.split(","); let vec: Vec<_> = split.collect(); println!("{} {:?}", line, vec); let number: i32 = vec[1].trim().parse().unwrap(); map.insert(vec[0], number); } } fn main() { let mut map = GUESS_NUMBER.borrow_mut(); map.insert("Tom", 3); map.insert("John", 7); if map.contains_key("John") { println!("John's number={:?}", map.get("John").unwrap()); } read_csv(); let map = GUESS_NUMBER.borrow(); for (k, v) in map.iter() { println!("{} {:?}", k, v); } }
注意事项:
lazy_static用于延迟初始化全局静态变量,避免编译时初始化问题RefCell提供内部可变性,允许在不可变引用下修改数据(仅单线程安全)- 全局变量会降低代码可测试性和可读性,仅在必要时使用
内容的提问来源于stack exchange,提问作者Daniel YC Lin
相关产品推荐
相关产品推荐

