向非引用类型添加生命周期约束
Adding lifetime constraints to non-reference types
我正在尝试弄清楚如何应用 Rust 生命周期来为 Erlang NIF 模块添加一些编译时实施。 NIF 模块是通常用 C 语言编写的提供扩展的共享库。
您将用 C 编写的回调的简化原型如下所示:
Handle my_nif_function(Heap *heap, Handle handle);
您获得了句柄和指向拥有它的堆的指针。在您的回调中,您可以检查输入句柄,在堆上创建更多句柄,并且 return 其中之一作为函数 return。堆及其所有句柄在回调 returns 后变为无效,因此您不得在回调期间存储堆或其句柄的副本。不幸的是,我看到人们正是这样做的,它最终导致了一个神秘的模拟器崩溃。 Rust 可以强制执行这些生命周期约束吗?
我认为可以通过将堆转换为引用来轻松管理堆。
fn my_nif_function(heap: &Heap, handle: Handle) -> Handle
但是我如何 link 堆的输入和输出句柄的生命周期?
另一个问题是您还可以创建自己的堆和句柄,允许它们存在于回调调用的范围之外。在 C++ 中,我会将 std::unique_ptr
与自定义析构函数一起使用。什么是 Rust 等价物?用于管理堆的 [简化] C API 如下所示:
Heap *create_heap();
void destroy_heap(Heap *);
参考:此处描述了 NIF:http://www.erlang.org/doc/man/erl_nif.html。 "heaps" 和 "handles" 的 Erlang 名称是 "environments" 和 "terms"。我使用了名称 "heaps" 和 "handles" 以便更广泛地理解这个问题。
生锈 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 页面,这可能会提供一些小帮助。我按照您最有可能使用它们的顺序列出它们:
我正在尝试弄清楚如何应用 Rust 生命周期来为 Erlang NIF 模块添加一些编译时实施。 NIF 模块是通常用 C 语言编写的提供扩展的共享库。
您将用 C 编写的回调的简化原型如下所示:
Handle my_nif_function(Heap *heap, Handle handle);
您获得了句柄和指向拥有它的堆的指针。在您的回调中,您可以检查输入句柄,在堆上创建更多句柄,并且 return 其中之一作为函数 return。堆及其所有句柄在回调 returns 后变为无效,因此您不得在回调期间存储堆或其句柄的副本。不幸的是,我看到人们正是这样做的,它最终导致了一个神秘的模拟器崩溃。 Rust 可以强制执行这些生命周期约束吗?
我认为可以通过将堆转换为引用来轻松管理堆。
fn my_nif_function(heap: &Heap, handle: Handle) -> Handle
但是我如何 link 堆的输入和输出句柄的生命周期?
另一个问题是您还可以创建自己的堆和句柄,允许它们存在于回调调用的范围之外。在 C++ 中,我会将 std::unique_ptr
与自定义析构函数一起使用。什么是 Rust 等价物?用于管理堆的 [简化] C API 如下所示:
Heap *create_heap();
void destroy_heap(Heap *);
参考:此处描述了 NIF:http://www.erlang.org/doc/man/erl_nif.html。 "heaps" 和 "handles" 的 Erlang 名称是 "environments" 和 "terms"。我使用了名称 "heaps" 和 "handles" 以便更广泛地理解这个问题。
生锈 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 页面,这可能会提供一些小帮助。我按照您最有可能使用它们的顺序列出它们: