在系统中只创建一个实例的结构创建和使用的最佳方法是什么?是的,这是必要的,它是OpenGL子系统,制作多个副本并将其传递到各处会增加混乱,而不是减轻它.
单身人士需要尽可能高效.似乎不可能在静态区域上存储任意对象,因为它包含Vec带有析构函数的对象.第二个选项是在静态区域存储(不安全)指针,指向堆分配单例.什么是最方便和最安全的方法,同时保持语法简洁.
She*_*ter 122
非答案答案
一般避免全球状态.相反,在早期(可能在main)的某处构建对象,然后将对该对象的可变引用传递到需要它的位置.这通常会使您的代码更易于推理,并且不需要向后弯曲.
在决定你想要全局可变变量之前,先在镜子里仔细看看.在极少数情况下它很有用,所以这就是为什么它值得知道该怎么做.
还想做一个......?
在慵懒的静电箱可以带走一些创造了单(下图)的苦差事.这是一个全局可变向量:
#[macro_use]
extern crate lazy_static;
use std::sync::Mutex;
lazy_static! {
    static ref ARRAY: Mutex<Vec<u8>> = Mutex::new(vec![]);
}
fn do_a_call() {
    ARRAY.lock().unwrap().push(1);
}
fn main() {
    do_a_call();
    do_a_call();
    do_a_call();
    println!("called {}", ARRAY.lock().unwrap().len());
}
如果你删除了Mutex那么你有一个没有任何可变性的全局单例.
如果您只需要跟踪整数值,则可以直接使用原子:
use std::sync::atomic::{AtomicUsize, Ordering};
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
fn do_a_call() {
    CALL_COUNT.fetch_add(1, Ordering::SeqCst);
}
fn main() {
    do_a_call();
    do_a_call();
    do_a_call();
    println!("called {}", CALL_COUNT.load(Ordering::SeqCst));
}
这很大程度上来自于Rust 1.0的实现stdin.你还应该看看现代的实现io::Lazy.我已经评论了每行的内容.
use std::sync::{Arc, Mutex, Once, ONCE_INIT};
use std::time::Duration;
use std::{mem, thread};
#[derive(Clone)]
struct SingletonReader {
    // Since we will be used in many threads, we need to protect
    // concurrent access
    inner: Arc<Mutex<u8>>,
}
fn singleton() -> SingletonReader {
    // Initialize it to a null value
    static mut SINGLETON: *const SingletonReader = 0 as *const SingletonReader;
    static ONCE: Once = ONCE_INIT;
    unsafe {
        ONCE.call_once(|| {
            // Make it
            let singleton = SingletonReader {
                inner: Arc::new(Mutex::new(0)),
            };
            // Put it in the heap so it can outlive this call
            SINGLETON = mem::transmute(Box::new(singleton));
        });
        // Now we give out a copy of the data that is safe to use concurrently.
        (*SINGLETON).clone()
    }
}
fn main() {
    // Let's use the singleton in a few threads
    let threads: Vec<_> = (0..10)
        .map(|i| {
            thread::spawn(move || {
                thread::sleep(Duration::from_millis(i * 10));
                let s = singleton();
                let mut data = s.inner.lock().unwrap();
                *data = i as u8;
            })
        })
        .collect();
    // And let's check the singleton every so often
    for _ in 0u8..20 {
        thread::sleep(Duration::from_millis(5));
        let s = singleton();
        let data = s.inner.lock().unwrap();
        println!("It is: {}", *data);
    }
    for thread in threads.into_iter() {
        thread.join().unwrap();
    }
}
打印出:
It is: 0
It is: 1
It is: 1
It is: 2
It is: 2
It is: 3
It is: 3
It is: 4
It is: 4
It is: 5
It is: 5
It is: 6
It is: 6
It is: 7
It is: 7
It is: 8
It is: 8
It is: 9
It is: 9
It is: 9
此代码使用Rust 1.23.0编译.真正的实现Stdin使用一些不稳定的功能来尝试释放分配的内存,这个代码没有.
真的,你可能想要制作SingletonReader工具Deref,DerefMut所以你不必戳入对象并自己锁定它.
所有这些工作都是lazy-static为您所做的.
Dan*_*ger 28
从 Rust 1.63 开始,使用全局可变单例会更容易,尽管在大多数情况下仍然最好避免全局变量。
现在,您可以使用全局静态锁Mutex::new,而无需延迟初始化:constMutex
use std::sync::Mutex;
static GLOBAL_DATA: Mutex<Vec<i32>> = Mutex::new(Vec::new());
fn main() {
    GLOBAL_DATA.lock().unwrap().push(42);
    println!("{:?}", GLOBAL_DATA.lock().unwrap());
}
请注意,这还取决于 的Vec::new事实const。如果您需要使用非const函数来设置单例,您可以将数据包装在 中Option,并最初将其设置为None。Hashset这允许您使用当前无法在上下文中使用的数据结构const:
use std::sync::Mutex;
use std::collections::HashSet;
static GLOBAL_DATA: Mutex<Option<HashSet<i32>>> = Mutex::new(None);
fn main() {
    *GLOBAL_DATA.lock().unwrap() = Some(HashSet::from([42]));
    println!("V2: {:?}", GLOBAL_DATA.lock().unwrap());
}
或者,您可以使用RwLock代替 a Mutex,因为从 Rust 1.63 开始也是如此RwLock::new。const这使得同时从多个线程读取数据成为可能。
如果您需要使用非const函数进行初始化并且您不希望使用 an ,则可以使用像once_cell或lazy-staticOption这样的包来进行延迟初始化,如Shepmaster 的回答中所述。
小智 6
回顾一下:不要使用对象更改其内部状态的内部可变性,而是考虑使用一种模式,将新状态提升为当前状态,并且旧状态的当前使用者将通过将 Arc 放入 RwLock 来继续保留它。
use std::sync::{Arc, RwLock};
#[derive(Default)]
struct Config {
    pub debug_mode: bool,
}
impl Config {
    pub fn current() -> Arc<Config> {
        CURRENT_CONFIG.with(|c| c.read().unwrap().clone())
    }
    pub fn make_current(self) {
        CURRENT_CONFIG.with(|c| *c.write().unwrap() = Arc::new(self))
    }
}
thread_local! {
    static CURRENT_CONFIG: RwLock<Arc<Config>> = RwLock::new(Default::default());
}
fn main() {
    Config { debug_mode: true }.make_current();
    if Config::current().debug_mode {
        // do something
    }
}