使结构的生存期超过赋予该结构的方法的参数

问题描述 投票:1回答:1

我正在寻找一种方法,以确保结构寿命超过赋予该结构方法的参数。即使该结构在离开方法后不保留对该数据的引用。

这是用于将原始指针包装到FFI的。我想保证实现FFI的结构的寿命超过了我用来将Rust对象馈送到指针包装程序的Option<&'a Any>

Context是FFI包装器。Data具有映射到FFI类型的不同类型。 FFI函数会在返回之前立即复制所有这些类型。

除了原始指针。

所以我将寿命指定符添加到Context中,并在send_data()中使用它。

但是以某种方式,这还不够。我希望下面的代码无法编译。

编辑:Rust Discord建议有人在&self中使mut send_data()可用。这具有理想的效果,但是我的FFI是线程安全的(无状态),并且send_data()是时间紧迫的。因此,我非常想避免这种情况。

use std::any::Any;
use std::marker::PhantomData;

struct IntegerArray<'a> {
    data: &'a [i32],
}

struct WrappedRawPointer<'a> {
    ptr: *const std::ffi::c_void,
    _marker: PhantomData<&'a ()>,
}

impl<'a> WrappedRawPointer<'a> {
    fn new(data: Option<&'a dyn Any>) -> Self {
        Self {
            ptr: data
                .map(|p| p as *const _ as *const std::ffi::c_void)
                .unwrap_or(std::ptr::null()),
            _marker: PhantomData,
        }
    }
}

enum Data<'a, 'b> {
    IntegerArray(IntegerArray<'a>),
    WrappedRawPointer(WrappedRawPointer<'b>),
}

struct Context<'a> {
    ctx: u32,
    _marker: PhantomData<&'a ()>,
}

impl<'a> Context<'a> {
    fn new() -> Self {
        Self {
            ctx: 0, // Call FFI to initialize context
            _marker: PhantomData,
        }
    }

    fn send_data(&self, data: Data<'_, 'a>) {
        match data {
            Data::IntegerArray(_i) => (),      // Call FFI function
            Data::WrappedRawPointer(_p) => (), // Call FFI function
        }
    }
}

fn main() {
    let ctx = Context::new();

    {
        let some_float: f32 = 42.0;
        ctx.send_data(
            Data::WrappedRawPointer(
                WrappedRawPointer::new(
                    Some(&some_float)
                )
            )
        );

        // I would like rustc to complain 
        // here that some_float does not
        // outlive ctx
    }

    // Explicitly drop outside
    // the previous block to 
    // prevent rustc from being
    // clever
    drop(ctx);
}
rust lifetime ffi
1个回答
0
投票

使send_data&mut self而不是&self是可行的,因为相对于类型self,它使Self参数invariant的类型。 Subtyping and Variance在Rustonomicon中进行了描述,以及此处有关堆栈溢出的其他问题(请参见下文)。

因为即使在self是不可变的引用时也要保持不变,所以这表明Context<'a> itself]的方差是错误的:它在'a中是协变的,但它应该是不变的。您可以通过将PhantomData的类型参数更改为在'a中也不变的内容来解决此问题:

struct Context<'a> {
    ctx: u32,
    _marker: PhantomData<*mut &'a ()>,  // or Cell<&'a ()>, or fn(&'a ()) -> &'a (), etc.
}

PhantomData不仅仅是您机械地添加的内容,以使编译器不会对您大吼大叫。 PhantomData的类型参数的特定形式告诉编译器如何

您的结构与其类型和生存期参数有关(当编译器无法自行解决时)。在这种情况下,您想告诉编译器Context<'some_long_lifetime> cant可以代替Context<'a_much_shorter_lifetime>,即使其所有字段都允许该替换。

关于方差的其他问题

  • [How do I share a struct containing a phantom pointer among threads?(如果Context应该为SendSync,可能是相关的]
  • © www.soinside.com 2019 - 2024. All rights reserved.