是否有可能在编译时确保给定结构在程序生命周期中的任何给定时间点最多仅存在一次?

问题描述

我正在包装一个较旧的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失败,则需要做一些额外的簿记操作,以解除锁定或中毒(例如,向程序发出信号,表示灾难性失败,并且没有新的{{1} })。

除了解决您的特定问题外,持有指向C端值的唯一指针是所有权,而不是引用。它基本上只是一个MyStruct。您不希望在其中存在生命周期或引用,而是想直接保存原始指针,可能是NonNull