इस कोड में, 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() और एक RefCell के साथ लिखने का भी प्रयास किया लेकिन मुझे उस संस्करण के साथ B को प्रारंभ करने के लिए A का उपयोग करने में परेशानी हुई।

अधिक सामान्य शब्दों में, प्रश्न हो सकता है "रस्ट में किसी प्रोग्राम के इनिटवार्स सेक्शन में सामान कैसे प्राप्त करें?"

यदि आप मुझे दिखा सकते हैं कि B को सीधे कैसे प्रारंभ किया जाए (init_tables() जैसे फ़ंक्शन के बिना), तो आपका उत्तर शायद सही है।

यदि init_tables() जैसा कोई फ़ंक्शन अपरिहार्य है, तो क्या मेरे प्रोग्राम में unsafe कूड़े को कम करने के लिए एक्सेसर फ़ंक्शन जैसी कोई चाल है?

1
BitTickler 1 जिंदा 2020, 12:42

1 उत्तर

सबसे बढ़िया उत्तर

जंग में एक कार्यक्रम के 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

इसलिए स्रोत कोड स्तर पर 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 उतना जटिल नहीं है। इसमें 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));
}
2
edwardw 2 जिंदा 2020, 04:00