单例Singleton能让您确保仅存在一个同类对象,同时提供对该实例的全局访问点。
Singleton 是一个全局可变对象,就Rust而言, 它是一个static mut项目,这反过来意味着 它需要一个unsafe块 来读取或写入可变静态变量。
一方面,它可以被视为不安全的模式;
但另一方面,Singleton 在实践中被用于 Rust。
在Rust中实现Singleton的一个纯粹安全的方法是完全不使用全局变量,通过函数参数传递一切。这非常类似Java中提倡的。
//! 在Rust中实现Singleton的一个纯粹安全的方法是不使用静态变量 //! 并通过函数参数传递一切。 //! 最古老的活体变量是在`main()`开始时创建的一个对象。
fn change(global_state: &mut u32) { *global_state += 1; }
fn main() { let mut global_state = 0u32;
change(&mut global_state);
println! ("最终状态: {}", global_state); }
|
从Rust 1.63开始,Mutex::new是常量,你可以使用全局静态Mutex锁而不需要懒惰的初始化。见下面的mutex.rs例子。
//!Ructc 1.63 //! https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton //! //! 从Rust 1.63开始,使用全局可变的单例 //虽然在大多数情况下,避免使用全局变量仍然是最好的选择。 //!情况下,还是要避免使用全局变量。 //! //! 现在`Mutex::new`是`const`,你可以使用全局静态的`Mutex`锁了 //! 而不需要懒惰的初始化。
use std::sync::Mutex。
static ARRAY: Mutex<Vec<i32>> = Mutex::new(Vec::new() )。
fn do_a_call() { ARRAY.lock().unwrap().push(1)。 }
fn main() { do_a_call(); do_a_call(); do_a_call();
println! ("Called {} times", ARRAY.lock().unwrap().len() ); }
|
还有一种懒惰单例:声明一个静态变量,并在第一次访问时进行懒惰初始化。
它实际上是通过不安全的静态Mut操作来实现的,然而,它使你的代码没有不安全的块。
lazy.rs:
//! 取自: https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton //! //! Rust并不允许没有`unsafe`的单子模式,因为它 //! 没有一个安全的可变全局状态。 //! //! `lazy-static`允许声明一个静态变量,并在第一次访问时进行懒惰初始化 //! 第一次访问时。它实际上是通过`unsafe`与`static mut`实现的。 //! 操作,然而,它使你的代码没有`不安全`块。 //! //! `Mutex`提供对单个对象的安全访问。
use lazy_static::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() ); }
|
以上单例模式的理解侧重于全局变量的角度,其实单例还存在多线程模式下单线程执行的情况:
在任何编程语言中,单例一般都不容易安全实现,尤其是在多线程环境中。然而,Rust强调(可能是保证)内存和并发安全,再加上一些额外的语言约束,使得单例的实现在最好的情况下也很难,在最坏的情况下也很烦人。
在单例设计过程中必须做出几个决定:
- 初始化(何时/如何/何处;静态/惰性;一次/多次;可配置或硬编码)
- 可用性(始终可用或不可用,与初始化有关)
- 可变/不可变单例状态
- 可访问性/可发现性(全局可访问与否;多线程安全与否)
- 销毁(是否有特殊要求,何时/如何/何地)
- 对测试的影响
- (防止多次实例化)
详细见这里单例在并发编程中防止多次实例化:
Java并发编程中双重检查锁漏洞