rust - 有没有办法强制从特定堆栈帧返回后不使用 Rust 原始指针?

标签 rust ffi lifetime

我正在为(主要是 C 风格的)C++ 插件 SDK 编写 Rust 包装器。插件宿主是运行事件循环的图形桌面应用程序。该插件会作为该事件循环的一部分定期调用。无论何时发生这种情况,插件都具有控制权并可以调用任意宿主函数。

我想包装的一个 C 函数返回一个原始指针。在该函数返回后,指针保证是一个有效的 C 字符串,因此取消引用它是安全的。但是,在插件回调返回后(从而将控制权交还给宿主),指针可能会过时。我如何为此编写一个符合人体工程学的函数包装器,它不会在某些时候导致未定义的行为,例如消费者何时尝试在下一个事件循环周期中访问字符串?

我考虑过以下方法:

1。返回一个拥有的字符串

我可以立即取消对指针的引用并将内容复制到一个拥有的 CString 中:

pub fn get_string_from_host() -> CString {
    let ptr: *const c_char = unsafe { ffi.get_string() };
    unsafe { CStr::from_ptr(ptr).to_owned() }
}

这是冒昧的——也许我的包装器的使用者对获得拥有的字符串不感兴趣,因为他们只想进行比较(这甚至是我会说的主要用例)。那时复制字符串完全是浪费。

2。返回原始指针

pub fn get_string_from_host() -> *const c_char {
    unsafe { ffi.get_string() }
}

这只会将问题转移给消费者。

3。返回一个 CStr 引用(不安全的方法)

pub unsafe fn get_string_from_host<'a>() -> &'a CStr {
    let ptr: *const c_char = ffi.get_string();
    CStr::from_ptr(ptr)
}

这是不安全的,因为引用的生命周期不准确。稍后访问引用可能会导致未定义的行为。将问题转移给消费者的另一种方式。

4。关闭而不是返回一些东西

pub fn with_string_from_host<T>(f: impl Fn(&CStr) -> T) -> T {
    let ptr: *const c_char = unsafe { ffi.get_string() };
    f(unsafe { CStr::from_ptr(ptr) })
}

pub fn consuming_function() {
    let length = with_string_from_host(|s| s.to_bytes().len());
}

这行得通,但确实需要习惯。


这些解决方案都不是真正令人满意的。

有没有办法确保“立即”使用返回值,这意味着它不会存储在任何地方或永远不会超出调用者的范围?

这听起来像是引用/生命周期的工作,但我不知道任何生命周期注释意味着“仅在当前堆栈框架中有效”。如果有的话,我会使用它(仅用于说明):

pub fn get_string_from_host() -> &'??? CStr {
    let ptr: *const c_char = unsafe { ffi.get_string() };
    unsafe { CStr::from_ptr(ptr) }
}

pub fn consuming_function() {
    // For example, this shouldn't be possible in this case
    let prolonged: &'static CStr = get_string_from_host();
    // But this should
    let owned = get_string_from_host().to_owned();
}

最佳答案

您的问题和评论列出了您的选择。它主要归结为满足其他人的期望,即最不意外规则。这主张返回一个拥有的 String。如前所述,拥有的 String 涉及一个副本(除非在循环中调用无数次,否则对性能的影响可以忽略不计)

我强烈建议不要使用 raw-pointer- 和 CStr-reference-solutions,它们是脚枪。

就个人而言,我会选择闭包,因为它实现了基本情况:访问字符串的代码的上下文必须移动到字符串所在的位置;我们不能让字符串移动到上下文所在的位置(据我们所知,即使是调用者也可能无法控制)。 闭包解决方案应该让你既能吃蛋糕又能吃:impl Fn(&CStr) -> T 类型的闭包可以是 |s| s.to_owned(),如果需要,使 with_string_from_host 返回一个副本。

关于rust - 有没有办法强制从特定堆栈帧返回后不使用 Rust 原始指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61106587/

相关文章:

haskell - 将 OCaml 代码与共享库链接

c++ - c++中的引用范围是什么?

struct - 添加到 struct/impl 的生命周期使模块无法解析

loops - 循环有生命周期是什么意思?

rust - 如何将迭代器适配器与返回impl Trait作为IntoIterator的IntoIter关联类型的函数一起使用?

rust - 如何设置非标准 gstreamer 属性的类型?

rust - 如何为简单结构实现 `Index` 特征?

multithreading - 让 Rust 线​​程摆脱阻塞操作的标准方法是什么?

c - C(不是 C++)的有限域(Galois 域)线性代数库

c - 如何在 Rust 中获取 C 指针的所有权并适本地删除它?