簡體   English   中英

我可以返回一個從特征實現中使用PhantomData的結構,以在不污染接口的情況下為原始指針添加生存期嗎?

[英]Can I return a struct which uses PhantomData from a trait implementation to add a lifetime to a raw pointer without polluting the interface?

這個問題中,有人評論說您可以使用PhantomData在結構內部添加綁定到原始指針的生存期。 我以為我會嘗試在我正在研究的現有代碼上執行此操作。

這是我們(最小化)的起點。 編譯( 操場 ):

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並在結構死亡后使用。 在這種情況下,由於Drop實現,基礎緩沖區將被釋放。

通過使用PhantomData我們可以確保只能獲取對buf引用,並且這些引用的生命周期都綁定到MyTrace實例從何處來。

我們可以這樣進行( 游樂場 ):

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界面的情況下使其工作? 具體來說,是否沒有在返回類型中添加生存期?

我將忽略您的直接問題,因為我相信您是在誤解了幾個初始步驟之后才提出的。

理論上講,我可以將buf指針移出MyTrace並在結構死亡后使用

復制指針,不移動指針,但是可以。

通過使用PhantomData我們可以確保僅獲取對buf引用

這不是真的。 即使您添加了PhantomData仍然很容易獲得原始指針的副本並濫用它。

在這種情況下,我是否正確理解PhantomData的動機?

PhantomData當您想要表現出某種類型的值而實際上卻沒有該值時,將使用PhantomData 有東西要參考假裝有什么參考是唯一有用的。 在您的示例中沒有這樣的值可供參考。

Rust文檔說了一些有關原始指針和PhantomData ,但我也許弄錯了

該示例實際上很好地說明了我的觀點。 Slice類型旨在表現為好像引用了從中借用的Vec

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

由於這Slice類型實際上並沒有一個參考,它需要一個PhantomData 行動象它有一個參考。 請注意,壽命'a不僅由整塊布組成-它還與現有值( Vec )有關。 Vec移動之后,這將導致Slice內存不安全,因此有意義的是包括Vec的生存期。

為什么在另一個問題中的評論者建議我使用PhantomData來提高我的原始指針的類型安全性

您可以使用PhantomData來提高用作引用的原始指針的安全性,但是您的引用中沒有現有的Rust值。 如果指針在引用后面具有某些值,那么您也可以使用它來確保正確性,您的指針似乎也是如此。 但是,由於它是c_void ,所以它並不是真正有用的。 您通常將其視為PhantomData<MyOwnedType>

錯誤消息中的'static來自哪里?

為什么需要使用加號運算符(Iterator <Item =&Foo> +'a)向特征添加生命周期?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM