rust - 有没有办法在不使其成为静态 mut 的情况下初始化非平凡的静态 std::collections::HashMap ?
问题描述
在这段代码中,A
不需要是static mut
,但编译器强制B
是static mut
:
use std::collections::HashMap;
use std::iter::FromIterator;
static A: [u32; 21] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
];
static mut B: Option<HashMap<u32, String>> = None;
fn init_tables() {
let hm = HashMap::<u32, String>::from_iter(A.iter().map(|&i| (i, (i + 10u32).to_string())));
unsafe {
B = Some(hm);
}
}
fn main() {
init_tables();
println!("{:?} len: {}", A, A.len());
unsafe {
println!("{:?}", B);
}
}
这是我发现接近我真正想要的东西的唯一方法:一个全局的、不可变HashMap
的,可供多个函数使用,而不会用unsafe
块乱扔我的所有代码。
我知道全局变量对于多线程应用程序来说是个坏主意,但我的是单线程的,那么我为什么要为永远不会发生的可能性付出代价呢?
由于我rustc
直接使用而不是使用cargo
,所以我不希望像lazy_static
. 我试图破译该包中的宏的作用,但没有结束。
我也尝试用thread_local()
和 a来写这个,RefCell
但我在用那个版本A
初始化时遇到了麻烦。B
更笼统地说,问题可能是“如何将东西放入 Rust 程序的 initvars 部分?”
如果你能告诉我如何B
直接初始化(没有类似的函数init_tables()
),你的答案可能是正确的。
如果像这样的函数init_tables()
是不可避免的,是否有像访问函数这样的技巧来减少unsafe
程序中的垃圾?
解决方案
如何将东西放入 Rust 程序的 initvars 部分?
结果rustc
将static
数据放在生成的二进制文件的.rodata
部分和static mut
数据中:.data
#[no_mangle]
static DATA: std::ops::Range<u32> = 0..20;
fn main() { DATA.len(); }
$ rustc static.rs
$ objdump -t -j .rodata static
static: file format elf64-x86-64
SYMBOL TABLE:
0000000000025000 l d .rodata 0000000000000000 .rodata
0000000000025490 l O .rodata 0000000000000039 str.0
0000000000026a70 l O .rodata 0000000000000400 elf_crc32.crc32_table
0000000000026870 l O .rodata 0000000000000200 elf_zlib_default_dist_table
0000000000026590 l O .rodata 00000000000002e0 elf_zlib_default_table
0000000000025060 g O .rodata 0000000000000008 DATA
0000000000027f2c g O .rodata 0000000000000100 _ZN4core3str15UTF8_CHAR_WIDTH17h6f9f810be98aa5f2E
因此,在源代码级别更改 fromstatic mut
会显着更改生成的二进制文件。static
该.rodata
部分是只读的,尝试写入它会使程序出现段错误。
如果 init_tables() 是审判日类别(不可避免)
这可能是不可避免的。由于默认.rodata
链接不起作用,因此必须直接控制它:
use std::collections::HashMap;
use std::iter::FromIterator;
static A: std::ops::Range<u32> = 0..20;
#[link_section = ".bss"]
static B: Option<HashMap<u32, String>> = None;
fn init_tables() {
let data = HashMap::from_iter(A.clone().map(|i| (i, (i + 10).to_string())));
unsafe {
let b: *mut Option<HashMap<u32, String>> = &B as *const _ as *mut _;
(&mut *b).replace(data);
}
}
fn main() {
init_tables();
println!("{:?} len: {}", A, A.len());
println!("{:#?} 5 => {:?}", B, B.as_ref().unwrap().get(&5));
}
我不想要像lazy_static这样的外部板条箱的“帮助”
其实lazy_static
没那么复杂。它巧妙地利用了这个Deref
特性。这是一个非常简化的独立版本,它比第一个示例更符合人体工程学:
use std::collections::HashMap;
use std::iter::FromIterator;
use std::ops::Deref;
use std::sync::Once;
static A: std::ops::Range<u32> = 0..20;
static B: BImpl = BImpl;
struct BImpl;
impl Deref for BImpl {
type Target = HashMap<u32, String>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
static LAZY: (Option<HashMap<u32, String>>, Once) = (None, Once::new());
LAZY.1.call_once(|| unsafe {
let x: *mut Option<Self::Target> = &LAZY.0 as *const _ as *mut _;
(&mut *x).replace(init_tables());
});
LAZY.0.as_ref().unwrap()
}
}
fn init_tables() -> HashMap<u32, String> {
HashMap::from_iter(A.clone().map(|i| (i, (i + 10).to_string())))
}
fn main() {
println!("{:?} len: {}", A, A.len());
println!("{:#?} 5 => {:?}", *B, B.get(&5));
}
推荐阅读
- c# - 如何修复xml文件的Unicode
- c# - 在 WPF 中 Windows 10 类型的 Toast 通知中显示通知图像
- javascript - 如何在力有向图 D3 中制作节点阴影?
- python - 使用 python 正则表达式提取项目编号
- http - 为什么 GET 请求不支持文件上传?
- mathematical-optimization - 如何使用 SCIP 解决 SAT 问题?
- ios - 如何使用 UITableViewCell 中的委托方法在标签中设置计算?
- android - 引起:net.sqlcipher.database.SQLiteException: file is not a database: ,编译时:select count(*) from sqlite_master
- reactjs - 将参数传递到后台屏幕并在本机反应中设置状态
- linux - Nexus 服务立即停止 2