是否有一种方法可以初始化非平凡的静态std :: collections :: HashMap而不使其变为静态mut?

问题描述 投票:1回答:1

在此代码中,A不必为static mut,但是编译器会强制Bstatic 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()RefCell编写此文件,但是我在使用A初始化该版本的B时遇到了麻烦。

更笼统地说,问题可能是“如何在Rust的程序的initvars部分中获取内容?”

[如果您可以向我展示如何直接初始化B(没有类似init_tables()的功能),您的答案可能是正确的。

如果像init_tables()这样的功能是不可避免的,是否有类似访问器功能的技巧可以减少程序中的unsafe垃圾?

rust global-variables
1个回答
2
投票

如何在Rust中将内容放入程序的initvars部分?

结果rustcstatic数据放在生成的二进制文件的.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

因此,在源代码级别从static 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));
}
© www.soinside.com 2019 - 2024. All rights reserved.