pointers - 我可以返回一个结构,该结构使用特征实现中的 PhantomData 来为原始指针添加生命周期而不污染接口(interface)吗?

标签 pointers memory-management rust traits lifetime

this question有人评论说您可以使用 PhantomData 添加绑定(bind)到结构内原始指针的生命周期。我想我会尝试在我一直在处理的现有代码段上执行此操作。

这是我们的(最小化)起点。这编译(playground):

extern crate libc;

use libc::{c_void, free, malloc};

trait Trace {}

struct MyTrace {
    #[allow(dead_code)]
    buf: *mut c_void,
}

impl MyTrace {
    fn new() -> Self {
        Self {
            buf: unsafe { malloc(128) },
        }
    }
}

impl Trace for MyTrace {}

impl Drop for MyTrace {
    fn drop(&mut self) {
        unsafe { free(self.buf) };
    }
}

trait Tracer {
    fn start(&mut self);
    fn stop(&mut self) -> Box<Trace>;
}

struct MyTracer {
    trace: Option<MyTrace>,
}

impl MyTracer {
    fn new() -> Self {
        Self { trace: None }
    }
}

impl Tracer for MyTracer {
    fn start(&mut self) {
        self.trace = Some(MyTrace::new());
        // Pretend the buffer is mutated in C here...
    }

    fn stop(&mut self) -> Box<Trace> {
        Box::new(self.trace.take().unwrap())
    }
}

fn main() {
    let mut tracer = MyTracer::new();
    tracer.start();
    let _trace = tracer.stop();
    println!("Hello, world!");
}

我认为上述代码的问题在于我可以理论上buf 指针移出MyTrace 并使用if在结构死亡之后。在这种情况下,由于 Drop 实现,底层缓冲区将被释放。

通过使用 PhantomData,我们可以确保只能获取对 buf 的引用,并且这些引用的生命周期绑定(bind)到 MyTrace 的实例 他们从哪里来。

我们可以这样进行(playground):

extern crate libc;

use libc::{c_void, free, malloc};
use std::marker::PhantomData;

trait Trace {}

struct MyTrace<'b> {
    #[allow(dead_code)]
    buf: *mut c_void,
    _phantom: PhantomData<&'b c_void>,
}

impl<'b> MyTrace<'b> {
    fn new() -> Self {
        Self {
            buf: unsafe { malloc(128) },
            _phantom: PhantomData,
        }
    }
}

impl<'b> Trace for MyTrace<'b> {}

impl<'b> Drop for MyTrace<'b> {
    fn drop(&mut self) {
        unsafe { free(self.buf) };
    }
}

trait Tracer {
    fn start(&mut self);
    fn stop(&mut self) -> Box<Trace>;
}

struct MyTracer<'b> {
    trace: Option<MyTrace<'b>>,
}

impl<'b> MyTracer<'b> {
    fn new() -> Self {
        Self { trace: None }
    }
}

impl<'b> Tracer for MyTracer<'b> {
    fn start(&mut self) {
        self.trace = Some(MyTrace::new());
        // Pretend the buffer is mutated in C here...
    }

    fn stop(&mut self) -> Box<Trace> {
        Box::new(self.trace.take().unwrap())
    }
}

fn main() {
    let mut tracer = MyTracer::new();
    tracer.start();
    let _trace = tracer.stop();
    println!("Hello, world!");
}

但这会给出错误:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:53:36
   |
53 |         Box::new(self.trace.take().unwrap())
   |                                    ^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime 'b as defined on the impl at 46:1...
  --> src/main.rs:46:1
   |
46 | impl<'b> Tracer for MyTracer<'b> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: ...so that the types are compatible:
           expected std::option::Option<MyTrace<'_>>
              found std::option::Option<MyTrace<'b>>
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::boxed::Box<Trace + 'static>
              found std::boxed::Box<Trace>

我有三个子问题:

  • 在这种情况下,我是否正确理解了 PhantomData 的动机?
  • 'static 在错误消息中来自哪里?
  • 是否可以在不更改stop 接口(interface)的情况下使其工作?具体来说,不为返回类型添加生命周期?

最佳答案

我将忽略你的直接问题,因为我相信你是在误解了几个初始步骤后才得出这个问题的。

I could in theory move the buf pointer out of a MyTrace and use if after the struct has died

复制指针,不是移动它,而是。

By using a PhantomData we can ensure that only references to buf can be obtained

这不是真的。即使添加了 PhantomData,也同样容易获取原始指针的副本并滥用它。 .

Did I understand the motivation for PhantomData in this scenario correctly?

没有。 PhantomData当你想表现得好像你有某种类型的值但实际上没有它时使用。假装引用某物只有在某物可以引用时才有用。在你的例子中没有这样的值(value)可以引用。

The Rust docs say something about raw pointers and PhantomData, but I perhaps got it wrong

这个例子实际上很好地说明了我的观点。 Slice类型旨在表现得好像它具有对 Vec 的引用它是从以下地方借来的:

fn borrow_vec<'a, T>(vec: &'a Vec<T>) -> Slice<'a, T>

自此Slice type 实际上没有引用,它需要一个 PhantomData 表现就像它有一个引用。请注意,生命周期 'a不只是由整 block 布组成——它与现有值 (Vec) 相关。这会导致 Slice 的内存不安全在 Vec 之后存在已经移动,因此包含 Vec 的生命周期是有意义的.

why the commenter in the other question suggested I use PhantomData to improve the type safety of my raw pointer

您可以使用 PhantomData提高作为引用的原始指针的安全性,但是你的指针没有一些现有的 Rust 值可以引用。如果您的指针在引用后面拥有一些值,您也可以使用它来确保正确性,而您的指针似乎确实如此。但是,由于它是 c_void ,其实用处不大。您通常会将其视为 PhantomData<MyOwnedType> .

Where is 'static coming from in the error message?

Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?

关于pointers - 我可以返回一个结构,该结构使用特征实现中的 PhantomData 来为原始指针添加生命周期而不污染接口(interface)吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50390682/

相关文章:

c - 只要您永远不取消引用它,持有未对齐的指针是否定义明确?

c - 下面的内存分配有什么不同吗?

rust - 如何从 GPIO 触发中断

generics - 如何实现涉及特征对象内置类型的通用可交换 std::ops?

.net - 64 位 CLR 可以使用压缩指针吗?

c - 指针直接分配给字符串?有什么缺点?

c++ - 类设计: how to return a shared_ptr: reference or copy

ios - 使用StoryBoard和ARC进行模态搜索后的废弃内存

rust - 包装一个借用某些东西的结构

c - 为什么调用接受 (const char * const **) 和 char *** 的函数会引发 -Wcast-qual 标志?