如何制作 Rust 单例的析构函数 运行?
How to make a Rust singleton's destructor run?
这些是我知道的在 Rust 中创建单例的方法:
#[macro_use]
extern crate lazy_static;
use std::sync::{Mutex, Once, ONCE_INIT};
#[derive(Debug)]
struct A(usize);
impl Drop for A {
fn drop(&mut self) {
// This is never executed automatically.
println!(
"Dropping {:?} - Important stuff such as release file-handles etc.",
*self
);
}
}
// ------------------ METHOD 0 -------------------
static PLAIN_OBJ: A = A(0);
// ------------------ METHOD 1 -------------------
lazy_static! {
static ref OBJ: Mutex<A> = Mutex::new(A(1));
}
// ------------------ METHOD 2 -------------------
fn get() -> &'static Mutex<A> {
static mut OBJ: *const Mutex<A> = 0 as *const Mutex<A>;
static ONCE: Once = ONCE_INIT;
ONCE.call_once(|| unsafe {
OBJ = Box::into_raw(Box::new(Mutex::new(A(2))));
});
unsafe { &*OBJ }
}
fn main() {
println!("Obj = {:?}", PLAIN_OBJ); // A(0)
println!("Obj = {:?}", *OBJ.lock().unwrap()); // A(1)
println!("Obj = {:?}", *get().lock().unwrap()); // A(2)
}
其中 None 在程序退出时调用 A
的析构函数 (drop()
)。这是方法 2(已分配堆)的预期行为,但我没有研究 lazy_static!
的实现就知道它会是相似的。
这里没有RAII。我可以在 C++ 中实现 RAII 单例的行为(我以前用 C++ 编写代码直到一年前,所以我的大部分比较都与它有关——我不知道很多其他语言)使用函数局部静态:
A& get() {
static A obj; // thread-safe creation with C++11 guarantees
return obj;
}
这可能是 allocated/created(懒惰地)在实现定义的区域中并且在程序的生命周期内有效。当程序终止时,析构函数是确定性的运行。我们需要避免从其他静态变量的析构函数中访问它,但我从来没有 运行 那样。
我可能需要释放资源,我希望 drop()
成为 运行。现在,我最终在程序终止之前手动完成它(在所有线程加入后接近 main 的末尾等)。
我什至不知道如何使用 lazy_static!
做到这一点,所以我避免使用它,只使用方法 2,我可以在最后手动销毁它。
我不想这样做;有没有办法让我在 Rust 中拥有这样一个 RAII 行为单例?
单例尤其是单例,总体上 constructors/destructors 是一种祸害(尤其是在 C++ 等语言中)。
我想说它们引起的主要(功能)问题分别称为 静态初始化(resp. 销毁)顺序失败 。也就是说,很容易意外地在这些全局变量之间创建一个依赖循环,即使没有这样的循环,编译器也不能立即清楚它们应该按什么顺序 built/destroyed.
它们还可能导致其他问题:启动速度变慢、意外共享内存、...
在Rust中,采取的态度一直是没有生命before/after主要。因此,尝试获得 C++ 行为可能不会按预期工作。
如果您满足以下条件,您将获得更好的语言支持:
- 删除全局方面
- 放弃拥有单个实例的尝试
(作为奖励,并行测试也会容易得多)
因此,我的建议是简单地坚持使用局部变量。在 main
中实例化它,通过 value/reference 向下调用堆栈传递它,不仅可以避免那些棘手的初始化顺序问题,还可以破坏。
这些是我知道的在 Rust 中创建单例的方法:
#[macro_use]
extern crate lazy_static;
use std::sync::{Mutex, Once, ONCE_INIT};
#[derive(Debug)]
struct A(usize);
impl Drop for A {
fn drop(&mut self) {
// This is never executed automatically.
println!(
"Dropping {:?} - Important stuff such as release file-handles etc.",
*self
);
}
}
// ------------------ METHOD 0 -------------------
static PLAIN_OBJ: A = A(0);
// ------------------ METHOD 1 -------------------
lazy_static! {
static ref OBJ: Mutex<A> = Mutex::new(A(1));
}
// ------------------ METHOD 2 -------------------
fn get() -> &'static Mutex<A> {
static mut OBJ: *const Mutex<A> = 0 as *const Mutex<A>;
static ONCE: Once = ONCE_INIT;
ONCE.call_once(|| unsafe {
OBJ = Box::into_raw(Box::new(Mutex::new(A(2))));
});
unsafe { &*OBJ }
}
fn main() {
println!("Obj = {:?}", PLAIN_OBJ); // A(0)
println!("Obj = {:?}", *OBJ.lock().unwrap()); // A(1)
println!("Obj = {:?}", *get().lock().unwrap()); // A(2)
}
其中 None 在程序退出时调用 A
的析构函数 (drop()
)。这是方法 2(已分配堆)的预期行为,但我没有研究 lazy_static!
的实现就知道它会是相似的。
这里没有RAII。我可以在 C++ 中实现 RAII 单例的行为(我以前用 C++ 编写代码直到一年前,所以我的大部分比较都与它有关——我不知道很多其他语言)使用函数局部静态:
A& get() {
static A obj; // thread-safe creation with C++11 guarantees
return obj;
}
这可能是 allocated/created(懒惰地)在实现定义的区域中并且在程序的生命周期内有效。当程序终止时,析构函数是确定性的运行。我们需要避免从其他静态变量的析构函数中访问它,但我从来没有 运行 那样。
我可能需要释放资源,我希望 drop()
成为 运行。现在,我最终在程序终止之前手动完成它(在所有线程加入后接近 main 的末尾等)。
我什至不知道如何使用 lazy_static!
做到这一点,所以我避免使用它,只使用方法 2,我可以在最后手动销毁它。
我不想这样做;有没有办法让我在 Rust 中拥有这样一个 RAII 行为单例?
单例尤其是单例,总体上 constructors/destructors 是一种祸害(尤其是在 C++ 等语言中)。
我想说它们引起的主要(功能)问题分别称为 静态初始化(resp. 销毁)顺序失败 。也就是说,很容易意外地在这些全局变量之间创建一个依赖循环,即使没有这样的循环,编译器也不能立即清楚它们应该按什么顺序 built/destroyed.
它们还可能导致其他问题:启动速度变慢、意外共享内存、...
在Rust中,采取的态度一直是没有生命before/after主要。因此,尝试获得 C++ 行为可能不会按预期工作。
如果您满足以下条件,您将获得更好的语言支持:
- 删除全局方面
- 放弃拥有单个实例的尝试
(作为奖励,并行测试也会容易得多)
因此,我的建议是简单地坚持使用局部变量。在 main
中实例化它,通过 value/reference 向下调用堆栈传递它,不仅可以避免那些棘手的初始化顺序问题,还可以破坏。