rust - 向非引用类型添加生命周期约束

标签 rust ffi lifetime

我正在尝试弄清楚如何应用 Rust 生命周期来为 Erlang NIF 模块添加一些编译时实现。 NIF 模块是通常用 C 语言编写的提供扩展的共享库。

用 C 编写的回调的简化原型(prototype)如下所示:

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 名称是“环境”和“术语”。我使用了名称“堆”和“句柄”,以便更广泛地理解这个问题。

最佳答案

使用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页,这可能是一些小的帮助。我按照您最有可能使用它们的顺序列出了它们:

关于rust - 向非引用类型添加生命周期约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28174681/

相关文章:

c++ - 将 C 库与 Haskell 库静态链接

ruby - 如何通过 Ruby 的 FFI 扩展使用 native C 代码的动态缓冲区指针

recursion - 不安全的 Rust 中的堆栈引用,但确保不安全不会从堆栈中泄漏?

c++ - 函数返回的字符串文字的生命周期

rust - 匹配元组作为映射的输入

rust - 为什么从方法返回可变引用会阻止调用任何其他方法,即使引用超出范围也是如此?

rust - 限制 Rust 中的对象生命周期

c++ - 双重构造是未定义的行为吗?

rust - 迭代获取子节点的可变引用

http - HTTP 响应的正文存储在哪里? (使用 Rust + reqwest)