【问题标题】:Restricting object lifetimes in Rust限制 Rust 中的对象生命周期
【发布时间】:2016-12-13 20:59:58
【问题描述】:

我正在包装一个 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 的生命周期系统来阻止上面的代码编译吗?

【问题讨论】:

  • 提问时请提供minimal reproducible example。呈现的代码失败,出现 7 个未定义项的错误。您也可以将其设为 M,同时将其设为 C

标签: rust ffi lifetime-scoping


【解决方案1】:

是的,只需使用正常的生命周期:

#[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 请注意,为create_object 选择&amp;mut self 意味着您不能创建第二个对象,因为看起来Context 仍然是可变借用的和/或您可以不要第二次改变它,因为它仍然有一个不可变的借用;不确定这是否符合您的期望。
  • 啊,这解释了我遇到的第二个问题。有没有办法删除mut-ness,同时仍然拥有&amp;mut self 的功能? context: self as &amp;Context, 之类的东西?
  • @Timmmm 它很大程度上取决于底层代码的作用......Object 实际上是否有指向Context 中某些内容的指针?如果Context 被移动会发生什么?多线程问题呢?底层代码是否处理这个问题?为什么要以&amp;mut self 开头?
  • Object 没有指向Context 的显式指针,但可能有一个内部指针(尚未检查)。如果Context 被移动,一切都很好,因为我没有导出CloneCopy。并且原始指针不是Send,因此无法跨线程移动任何内容。至少这是我的新手级理解。
猜你喜欢
  • 1970-01-01
  • 2013-07-03
  • 2017-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多