gpt4 book ai didi

rust - 有没有办法在不使它成为静态mut的情况下初始化一个非平凡的静态std::collections::HashMap?

转载 作者:行者123 更新时间:2023-12-03 11:35:59 24 4
gpt4 key购买 nike

在此代码中,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垃圾呢?

最佳答案

How to get stuff into the initvars section of a program in Rust?



结果 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节是只读的,尝试对其进行写操作将使程序故障。

If init_tables() is of the judgement day category (inevitable)



这可能是不可避免的。由于默认的 .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));
}

I don't want the "help" of extern crates like 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));
}

关于rust - 有没有办法在不使它成为静态mut的情况下初始化一个非平凡的静态std::collections::HashMap?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59551404/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com