使结构的寿命超过提供给该结构的方法的参数 [英] Making a struct outlive a parameter given to a method of that struct

查看:49
本文介绍了使结构的寿命超过提供给该结构的方法的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am looking for a way to ensure a struct outlives the parameter given to a method of that struct. Even if the struct doesn't hold a reference to that data after leaving the method.

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

This is for wrapped raw pointers fed to an FFI. I want to guarantee that the struct implementing the FFI outlives the Option<&'a Any> I use to feed the Rust object to the pointer wrapper.

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

Context is the FFI wrapper. Data holds different types that map to FFI types. The FFI functions copies all these types immediately before returning.

除了原始指针.

因此,我为这些代码在 Context 中添加了一个生存期说明符,并在 send_data()中使用了该说明符.

So I add a lifetime specifier to Context just for those and use that in send_data().

但是这还不够.我希望下面的代码无法编译.

But somehow this is not enough. I expected below code to not compile.

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

someone one the Rust Discord suggested making &self mutable in send_data(). This has the desired effect but my FFI is thread safe (and stateless) and send_data() is time critical. So I would very much like to avoid this.

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);
}

推荐答案

使 send_data 接受& mut self 而不是& self 之所以起作用,是因为相对于 Self 类型,它使 self 参数不变的类型成为可能.子类型和差异在Rustonomicon中进行了描述,以及其他有关此处的问题堆栈溢出(请参见下文).

Making send_data take &mut self instead of &self works because it makes the type of the self parameter invariant with respect to the type Self. Subtyping and Variance is described in the Rustonomicon, as well as other questions here on Stack Overflow (see below).

由于即使在 self 是不可变的引用时也要保持不变,因此这表明 Context<'a> 自身的方差是错误的:它在'a 中是协变的,但是应该是不变的.您可以通过将 PhantomData 的类型参数更改为在'a 中相同的东西来解决此问题:

Since you want invariance even when self is an immutable reference, that suggests that the variance of Context<'a> itself is wrong: it is covariant in 'a, but it should be invariant. You can fix this by changing the type argument to PhantomData to something that is also invariant in 'a:

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

PhantomData 不仅仅是您机械地添加的内容,以使编译器不会对您大吼大叫. PhantomData 的类型参数的特定形式告诉编译器如何您的结构与其类型和生存期参数相关(当编译器无法自行解决时).在这种情况下,您想告诉编译器,即使 Context<'some_long_lifetime> 也不能代替 Context<'a_much_shorter_lifetime> 尽管它的所有字段都将允许该替换.

PhantomData is not just something you add mechanically to make the compiler not yell at you. The specific form of the type argument to PhantomData tells the compiler how your struct is related to its type and lifetime parameters (when the compiler can't figure it out by itself). In this case you want to tell the compiler that a Context<'some_long_lifetime> can't be substituted for a Context<'a_much_shorter_lifetime> even though its fields would all allow that substitution.

  • How can this instance seemingly outlive its own parameter lifetime?
  • Why does linking lifetimes matter only with mutable references?
  • How do I share a struct containing a phantom pointer among threads? (may be relevant if Context should be Send or Sync)

这篇关于使结构的寿命超过提供给该结构的方法的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆