Rust中限制对象的生命周期

4

我正在封装一个C库,它有一个标准的上下文对象:

library_context* context = library_create_context();

然后,您可以使用它来创建更多的对象:

library_object* object = library_create_object(context);

并将它们都摧毁:

library_destroy_object(object);
library_destroy_context(context);

所以我已经将这个封装在Rust结构体中:

struct Context {
    raw_context: *mut library_context,
}

impl Context {
    fn new() -> Context {
        Context {
            raw_context: unsafe { library_create_context() },
        }
    }

    fn create_object(&mut self) -> Object {
        Object {
            raw_object: unsafe { library_create_object(self.raw_context) },
        }
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            library_context_destroy(self.raw_context);
        }
    }
}

struct Object {
    raw_object: *mut library_object,
}

impl Drop for Object {
    fn drop(&mut self) {
        unsafe {
            library_object_destroy(self.raw_object);
        }
    }
}

现在我可以这样做,看起来它能够工作:

fn main() {
    let mut ctx = Context::new();
    let ob = ctx.create_object();
}

然而,我也可以做到这一点:
fn main() {
    let mut ctx = Context::new();
    let ob = ctx.create_object();
    drop(ctx);

    do_something_with(ob);
}

即,库上下文在创建的对象之前被销毁。

我是否可以使用Rust的生命周期系统来防止上面的代码编译?


1
请在提问时提供一个 [MCVE]。目前呈现的代码存在 7 个未定义项的错误。同时,您也可以在保持其为 [C] 的同时让它更加 [M]。 - Shepmaster
1个回答

6

是的,只需使用普通生命周期:

#[derive(Debug)]
struct Context(u8);

impl Context {
    fn new() -> Context {
        Context(0)
    }

    fn create_object(&mut self) -> Object {
        Object {
            context: self,
            raw_object: 1,
        }
    }
}

#[derive(Debug)]
struct Object<'a> {
    context: &'a Context,
    raw_object: u8,
}

fn main() {
    let mut ctx = Context::new();
    let ob = ctx.create_object();
    drop(ctx);

    println!("{:?}", ob);
}

这将失败。
error[E0505]: cannot move out of `ctx` because it is borrowed
  --> src/main.rs:26:10
   |
25 |     let ob = ctx.create_object();
   |              --- borrow of `ctx` occurs here
26 |     drop(ctx);
   |          ^^^ move out of `ctx` occurs here

有时人们喜欢使用PhantomData,但我不确定这里有什么好处:
fn create_object(&mut self) -> Object {
    Object {
        marker: PhantomData,
        raw_object: 1,
    }
}

#[derive(Debug)]
struct Object<'a> {
    marker: PhantomData<&'a ()>,
    raw_object: u8,
}

啊哈,我尝试了那个,但有点不对。谢谢! - Timmmm
@Timmmm 注意,create_object 中选择 &mut self 的意思是你不能创建第二个对象,因为 Context 仍然被可变地借用和/或你不能第二次改变它,因为它仍然有一个不可变的借用;不确定这是否符合你的期望。 - Shepmaster
啊,这解释了我遇到的第二个问题。有没有办法在函数接受&mut self的同时去掉mut呢?比如像这样context: self as &Context, - Timmmm
@Timmmm 这很大程度上取决于底层代码的功能... Object 实际上是否有指向 Context 中某个东西的指针?如果 Context 被移动了会发生什么?多线程方面的问题又如何处理 - 底层代码是否处理了这些问题?为什么一开始就将其设置为 &mut self - Shepmaster
“Object”没有显式指向“Context”的指针,但可能有一个内部指针(尚未检查)。如果“Context”被移动,一切都没问题,因为我没有派生“Clone”或“Copy”。原始指针不是“Send”,因此无法在线程之间移动任何内容。至少这是我作为新手的理解。 - Timmmm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接