在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 aMyTrace
and use if after the struct has died
复制指针,不是移动它,而是。
By using a
PhantomData
we can ensure that only references tobuf
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/