将生命周期约束添加到非引用类型

时间:2015-01-27 15:59:02

标签: rust ffi lifetime

我试图找出如何应用Rust生命周期来为Erlang NIF模块添加一些编译时强制执行。 NIF模块是通常用C语言编写的共享库,提供扩展。

您在C中编写的回调的简化原型如下所示:

Handle my_nif_function(Heap *heap, Handle handle);

为您提供了一个句柄和指向拥有它的堆的指针。在回调中,您可以检查输入句柄,在堆上创建更多句柄,并在函数返回时返回其中一个句柄。回调返回后,堆及其所有句柄都将变为无效,因此在回调期间不得存储堆或其句柄的副本。不幸的是,我看到人们正是这样做的,最终导致了一个神秘的模拟器崩溃。 Rust可以强制执行这些生命周期限制吗?

认为可以通过将其转换为引用来轻松管理堆。

fn my_nif_function(heap: &Heap, handle: Handle) -> Handle

但是如何将输入和输出句柄的生命周期链接到堆?

另一个问题是你还可以创建自己的堆和句柄, 允许它们在回调调用范围之外生存。在C ++中,我会将std::unique_ptr与自定义析构函数一起使用。什么是Rust等价物?用于管理堆的[简化] C API如下所示:

Heap *create_heap();
void destroy_heap(Heap *);

参考:NIF在此处描述:http://www.erlang.org/doc/man/erl_nif.html。 “堆”和“句柄”的Erlang名称是“环境”和“术语”。我使用了“堆”和“句柄”的名称,以便更广泛地理解这个问题。

1 个答案:

答案 0 :(得分:2)

Rust 1.0

各种标记类型已统一为一个:PhantomData

use std::ptr;
use std::marker::PhantomData;

struct Heap {
    ptr: *const u8,
}

impl Heap {
    fn new(c_ptr: *const u8) -> Heap {
        Heap {
            ptr: c_ptr
        }
    }

    fn wrap_handle<'a>(&'a self, c_handle: *const u8) -> Handle<'a> {
        Handle {
            ptr: c_handle,
            marker: PhantomData,
        }
    }
}

struct Handle<'a> {
    ptr: *const u8,
    marker: PhantomData<&'a ()>, 
}

fn main() {
    let longer_heap = Heap::new(ptr::null());

    let handle = {
        let shorter_heap = Heap::new(ptr::null());

        let longer_handle = longer_heap.wrap_handle(ptr::null());
        let shorter_handle = shorter_heap.wrap_handle(ptr::null());

        // longer_handle // ok to return
        // shorter_handle // error: `shorter_heap` does not live long enough
    };
}

原始答案

以下是使用ContravariantLifetime的示例。我们将原始堆指针包装到一个结构中,然后将原始句柄指针包装在另一个结构中,重用堆的生命周期。

use std::ptr;
use std::marker::ContravariantLifetime;

struct Heap {
    ptr: *const u8,
}

impl Heap {
    fn new(c_ptr: *const u8) -> Heap {
        Heap {
            ptr: c_ptr
        }
    }

    fn wrap_handle<'a>(&'a self, c_handle: *const u8) -> Handle<'a> {
        Handle {
            ptr: c_handle,
            marker: ContravariantLifetime,
        }
    }
}

struct Handle<'a> {
    ptr: *const u8,
    marker: ContravariantLifetime<'a>,
}

fn main() {
    let longer_heap = Heap::new(ptr::null());

    let handle = {
        let shorter_heap = Heap::new(ptr::null());

        let longer_handle = longer_heap.wrap_handle(ptr::null());
        let shorter_handle = shorter_heap.wrap_handle(ptr::null());

        // longer_handle // ok to return
        // shorter_handle // error: `shorter_heap` does not live long enough
    };
}

终身标记

有3个生命标记。我不打算在这里尝试复制相当好但密集的文档,但也可以指出密集的Wikipedia页面,这可能是一些小帮助。我已按照您最有可能使用它们的顺序列出它们: