是否可以在编译时确保给定结构在程序生命周期的任何给定时间点最多只存在一次?
Is it possible to ensure at compile time that a given struct only exists at most once at any given point in time during the program lifecycle?
我正在包装一个旧的 C 库,它要求某些对象在程序执行期间的任何给定时间点只存在一次。
在 Rust 中是否可以在编译时保证结构的这种行为?
或者我应该研究如何创建单例并可能传递 Arc<MyWrapperStruct>
吗?
我已经研究过 std::sync::Once
,但这看起来像是一个工具,可以创建类似单例的东西,或者确保某些事情在应用程序生命周期内最多发生一次。
MyWrapperStruct
被多次实例化完全没问题,但编译器应该确保 MyWrapperStruct
永远不会同时存在(不同线程)或在同一范围内,以某种方式出现两次。
MyWrapperStruct
的后续实例化是合法的,只要先前的实例已被删除并超出范围。
例子
pub struct MyWrapperStruct<'base> {
pub base: &'base mut libc::c_void,
}
impl<'base> MyWrapperStruct<'base> {
pub fn new(logfile: &str) -> MyWrapperStruct<'base> {
let string = CString::new(logfile).unwrap();
let mut base: &mut libc::c_void;
unsafe {
base = &mut *ptr::null_mut();
// c-call here
call_to_c_lib(&mut base, string.as_ptr());
}
MyWrapperStruct { base }
}
}
fn should_not_compile() {
MyWrapperStruct::new("log1.txt");
MyWrapperStruct::new("log2.txt");
}
fn should_compile() {
{
MyWrapperStruct::new("log1.txt");
}
{
MyWrapperStruct::new("log2.txt");
}
}
您不能在编译时执行此操作,但在运行时您可以使用相对简单的原子跟踪器执行此操作。
use std::sync::atomic::{AtomicBool, Ordering};
static INSTANCE_EXISTS: AtomicBool = AtomicBool::new(false);
pub struct MyStruct {}
impl MyStruct {
pub fn try_new() -> Result<Self, &'static str> {
if !INSTANCE_EXISTS.compare_and_swap(false, true, Ordering::SeqCst) {
// Placeholder for C-side create object code
Ok(MyStruct {})
} else {
Err("Instance of MyStruct currently exists")
}
}
}
impl Drop for MyStruct {
fn drop(&mut self) {
// C-side destroy object code here
INSTANCE_EXISTS.store(false, Ordering::Release);
}
}
请注意,如果 MyStruct
的创建可能会失败,您将需要做一些额外的簿记工作以取消锁定或毒化它(即向程序发出信号,表明发生了灾难性故障并且没有新的 MyStruct
可以创建)。
为了解决您的 特定 问题,持有指向 C-side 值的唯一指针是所有权,而不是引用。它基本上只是一个Box
。你不想要生命周期或那里的引用,你想直接持有原始指针,可能是 NonNull
.
我正在包装一个旧的 C 库,它要求某些对象在程序执行期间的任何给定时间点只存在一次。
在 Rust 中是否可以在编译时保证结构的这种行为?
或者我应该研究如何创建单例并可能传递 Arc<MyWrapperStruct>
吗?
我已经研究过 std::sync::Once
,但这看起来像是一个工具,可以创建类似单例的东西,或者确保某些事情在应用程序生命周期内最多发生一次。
MyWrapperStruct
被多次实例化完全没问题,但编译器应该确保 MyWrapperStruct
永远不会同时存在(不同线程)或在同一范围内,以某种方式出现两次。
MyWrapperStruct
的后续实例化是合法的,只要先前的实例已被删除并超出范围。
例子
pub struct MyWrapperStruct<'base> {
pub base: &'base mut libc::c_void,
}
impl<'base> MyWrapperStruct<'base> {
pub fn new(logfile: &str) -> MyWrapperStruct<'base> {
let string = CString::new(logfile).unwrap();
let mut base: &mut libc::c_void;
unsafe {
base = &mut *ptr::null_mut();
// c-call here
call_to_c_lib(&mut base, string.as_ptr());
}
MyWrapperStruct { base }
}
}
fn should_not_compile() {
MyWrapperStruct::new("log1.txt");
MyWrapperStruct::new("log2.txt");
}
fn should_compile() {
{
MyWrapperStruct::new("log1.txt");
}
{
MyWrapperStruct::new("log2.txt");
}
}
您不能在编译时执行此操作,但在运行时您可以使用相对简单的原子跟踪器执行此操作。
use std::sync::atomic::{AtomicBool, Ordering};
static INSTANCE_EXISTS: AtomicBool = AtomicBool::new(false);
pub struct MyStruct {}
impl MyStruct {
pub fn try_new() -> Result<Self, &'static str> {
if !INSTANCE_EXISTS.compare_and_swap(false, true, Ordering::SeqCst) {
// Placeholder for C-side create object code
Ok(MyStruct {})
} else {
Err("Instance of MyStruct currently exists")
}
}
}
impl Drop for MyStruct {
fn drop(&mut self) {
// C-side destroy object code here
INSTANCE_EXISTS.store(false, Ordering::Release);
}
}
请注意,如果 MyStruct
的创建可能会失败,您将需要做一些额外的簿记工作以取消锁定或毒化它(即向程序发出信号,表明发生了灾难性故障并且没有新的 MyStruct
可以创建)。
为了解决您的 特定 问题,持有指向 C-side 值的唯一指针是所有权,而不是引用。它基本上只是一个Box
。你不想要生命周期或那里的引用,你想直接持有原始指针,可能是 NonNull
.