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

2023-12-08

我正在为(主要是 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涉及一个副本(除非在循环中调用无数次,否则对性能的影响可以忽略不计)

我强烈建议不要使用原始指针 - 并且CStr-参考解决方案,即脚枪。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有办法强制从特定堆栈帧返回后不使用 Rust 原始指针? 的相关文章

随机推荐

  • 在 WebView 中单击链接时加载本地 HTML 文件

    我有一个加载本地 HTML 文件的 WebView 如下所示 webView loadRequest NSURLRequest requestWithURL NSURL fileURLWithPath NSBundle mainBundle
  • java中只接受单个数字

    我正在编写一个简单的程序 它接受多个输入并显示最大的然后是第二大的 我唯一的问题是我希望程序只接受个位数 我知道这又回到了基础 但请耐心听我说 到目前为止我写的代码是 import javax swing JOptionPane publi
  • 在 Android 中运行时更新视图

    这个例子非常简单 我想让用户通过显示文本 canvas drawText 来了解应用程序正在做什么 然后 我的第一条消息出现 但其他消息则不出现 我的意思是 我有一个 setText 方法 但它不更新 onCreate Bundle bun
  • Gradle 构建失败并提示:找不到 org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.3

    我正在使用 Android Studio Canary 构建一个新项目 在设置 jet pack compose 的依赖项时 我将其作为构建输出 Execution failed for task app processDebugAndro
  • TKinter 样式和 Treeview 点击问题

    我在 TKinter 中的样式上遇到了困难 我的主要问题是您无法单击树视图中的任何内容 要进行测试 只需按 按测试 按钮即可 由于某种原因 style theme create 接管了我的树视图小部件 无论我尝试什么 我似乎都找不到解决方案
  • 如何检查Azure Function是否仍在运行

    我遇到一种情况 我必须定期调用 Azure 函数 当我调用该函数时 我需要检查天蓝色函数的状态 如果Azure函数正在运行 那么我需要推迟调用直到它完成 我正在尝试查看电子邮件队列 当电子邮件传入时 我需要使用 Amazon SES 发送电
  • 如何在 Obj-C 中打开和读取资源分支

    我有一个旧的 OS9 文件 其中包含带有 CODE 资源的资源叉 我想从 Obj C 读取该文件 我用类似的东西 NSFileHandle codeFile NSFileHandle fileHandleForReadingAtPath m
  • 如何在 python 中生成 nginx 安全链接

    如何使用 python 为 nginx 中的安全链接模块建立链接 我希望使用 nginx 来提供具有过期链接的安全文件 链接到 Nginx 维基 shadfc 答案中的代码有效 为了Python 3 需要进行一些修改 import base
  • 用 PHP 解决验证码问题?个人挑战?

    我想看看我是否可以使用 PHP 解决验证码 以应对一些个人挑战 没什么太难的 同一行中的单词 始终使用相同颜色的单词等 这是一个示例 http www simplyshows com img jpg 只是想看看会有多难 我的问题是 是否有基
  • 确定构造函数、初始化和重置方法的任务的最佳实践是什么

    尽管我是用 Java 设计的 但这是一个常见的 OOP 问题 我并不是想解决某个特定问题 只是想思考一些设计原则 根据我的经验 我养成了将对象设置分为三个阶段的习惯 目标是最大限度地减少 额外的工作 混乱的代码和受损的可扩展性 建造 所需采
  • subprocess.Popen 命令(反词)在 shell 与 Web 应用程序中产生不同的输出

    我让 Django 在标准 WSGI Apache httpd 组合上运行 我注意到当我在 shell 中运行代码与在浏览器中运行代码时 文件输出是不同的 我已经隔离了其他所有内容 但仍然遇到同样的问题 这是代码 def test anti
  • 如何将 JavaPairDStream 的结果写入 Spark Streaming 上的输出 kafka 主题?

    我正在寻找一种在输出 kafka 主题中编写 Dstream 的方法 只有当微批量 RDD 吐出一些东西时 我在 Java8 中使用 Spark Streaming 和 Spark streaming kafka 连接器 都是最新版本 我无
  • delphi 7中如何读取资源文件?

    我已经制作了资源文件 并已将其编译并与我的 delphi 单元链接 但是从该资源文件读取时我遇到了问题 implementation R dfm R stuff res stuff rc procedure TForm1 FormCreat
  • Firebase getDocument(querySnapshot)不起作用

    我面临着 Firebase 快照的问题 我已成功将我的 Fierbase 帐户与我的 Xcode 项目连接起来 我能够更改 Firestore 云中的数据 但我读不懂 这是我的功能 class UserService static func
  • 色彩空间转换

    我正在尝试编写一个程序 在用户输入图像后 他可以看到一些小的图像处理 更具体地说 我想将图像从 RGB 转换为 CMYK 和 YUV 然后在屏幕上仅显示这些颜色空间中的一个分量 即仅来自 CMY 的青色或来自 YUV 的 U 我设法实现了转
  • 在 iPhone 的 Objective-C 中解析 Java 属性文件

    我正在 iPhone SDK 中寻找一种读取属性文件 不是 XML 风格 的方法 例如 a comment a comment a a string b a string with escape sequences t n r space
  • Windows 上的 libgit2 可能存在 Rugged gem 安装问题

    我正在使用 Windows 和我的Gemfile有这一行 gem rugged 当我跑步时bundle install它给了我这个错误 Installing rugged 0 21 0 with native extensions Gem
  • java中的ArrayList打印最后插入的值?

    我有以下java类 package com picvik model import java util Date public class ViewAlbum private Integer albumid private String a
  • 将引号替换为“``”和“''”

    我有一个包含很多内容的文档 标记 但我想将其转换为在 TeX 中使用 TeX 使用 2 个 标记作为开始引号 使用 2 个 标记作为结束引号 我只想在以下情况下对这些进行更改 以偶数出现在单行上 例如 有 2 4 或 6 已上线 例如 Th
  • 有没有办法强制从特定堆栈帧返回后不使用 Rust 原始指针?

    我正在为 主要是 C 风格 C 插件 SDK 编写一个 Rust 包装器 插件主机是一个运行事件循环的图形桌面应用程序 该插件定期作为该事件循环的一部分被调用 每当这种情况发生时 插件就具有控制权并可以调用任意主机函数 我想要包装的一个 C