有没有办法强制从特定堆栈帧返回后不使用 Rust 原始指针?
Is there a way to enforce that a Rust raw pointer is not used after returning from a specific stack frame?
我正在为(主要是 C 风格的)C++ 插件 SDK 编写 Rust 包装器。插件宿主是运行事件循环的图形桌面应用程序。该插件会作为该事件循环的一部分定期调用。每当发生这种情况时,插件都具有控制权并可以调用任意宿主函数。
一个我想包装 returns 原始指针的 C 函数。在那个函数 returns 之后,指针保证是一个有效的 C 字符串,所以取消引用它是安全的。但是,在插件回调 returns(从而将控制权交还给主机)之后,指针可能会变得陈旧。我如何为此编写一个符合人体工程学的函数包装器,它不会在某些时候导致未定义的行为,例如当消费者尝试在下一个事件循环周期中访问字符串时?
我考虑过以下方法:
1。 Return 一个拥有的字符串
我可以立即取消引用指针并将内容复制到一个拥有的 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。 Return 原始指针
pub fn get_string_from_host() -> *const c_char {
unsafe { ffi.get_string() }
}
这只是把问题转移到了消费者身上。
3。 Return 一个 CStr
引用(不安全的方法)
pub unsafe fn get_string_from_host<'a>() -> &'a CStr {
let ptr: *const c_char = ffi.get_string();
CStr::from_ptr(ptr)
}
这是不安全的,因为引用的生命周期不准确。稍后访问引用可能会导致未定义的行为。将问题转移给消费者的另一种方式。
4。关闭而不是 returning something
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());
}
这可行,但确实需要习惯。
None 这些解决方案确实令人满意。
有没有办法确保使用 return 值 "immediately",这意味着它不会存储在任何地方或永远不会超出调用者的范围?
这听起来像是 references/lifetimes 的工作,但我不知道任何生命周期注释意味着 "valid just in the current stackframe"。如果有的话,我会用那个(只是为了说明):
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();
}
您的问题和评论列出了您的选择。它主要归结为满足其他人的期望,即最不意外规则。这主张 return 拥有一个 String
。如前所述,拥有的 String
涉及一个副本(除非在循环中调用无数次,否则对性能的影响可以忽略不计)
我强烈建议不要使用 raw-pointer- 和 CStr
-reference-solutions,它们是脚枪。
就个人而言,我会选择闭包,因为它实现了基本情况:访问字符串的代码的上下文必须移动到字符串所在的位置;我们不能让字符串移动到上下文 的位置(据我们所知,即使是调用者也可能无法控制)。
闭包解决方案应该让你既能吃蛋糕又能吃:类型 impl Fn(&CStr) -> T
的闭包可以是 |s| s.to_owned()
,如果需要,可以制作 with_string_from_host
return 的副本。
我正在为(主要是 C 风格的)C++ 插件 SDK 编写 Rust 包装器。插件宿主是运行事件循环的图形桌面应用程序。该插件会作为该事件循环的一部分定期调用。每当发生这种情况时,插件都具有控制权并可以调用任意宿主函数。
一个我想包装 returns 原始指针的 C 函数。在那个函数 returns 之后,指针保证是一个有效的 C 字符串,所以取消引用它是安全的。但是,在插件回调 returns(从而将控制权交还给主机)之后,指针可能会变得陈旧。我如何为此编写一个符合人体工程学的函数包装器,它不会在某些时候导致未定义的行为,例如当消费者尝试在下一个事件循环周期中访问字符串时?
我考虑过以下方法:
1。 Return 一个拥有的字符串
我可以立即取消引用指针并将内容复制到一个拥有的 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。 Return 原始指针
pub fn get_string_from_host() -> *const c_char {
unsafe { ffi.get_string() }
}
这只是把问题转移到了消费者身上。
3。 Return 一个 CStr
引用(不安全的方法)
pub unsafe fn get_string_from_host<'a>() -> &'a CStr {
let ptr: *const c_char = ffi.get_string();
CStr::from_ptr(ptr)
}
这是不安全的,因为引用的生命周期不准确。稍后访问引用可能会导致未定义的行为。将问题转移给消费者的另一种方式。
4。关闭而不是 returning something
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());
}
这可行,但确实需要习惯。
None 这些解决方案确实令人满意。
有没有办法确保使用 return 值 "immediately",这意味着它不会存储在任何地方或永远不会超出调用者的范围?
这听起来像是 references/lifetimes 的工作,但我不知道任何生命周期注释意味着 "valid just in the current stackframe"。如果有的话,我会用那个(只是为了说明):
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();
}
您的问题和评论列出了您的选择。它主要归结为满足其他人的期望,即最不意外规则。这主张 return 拥有一个 String
。如前所述,拥有的 String
涉及一个副本(除非在循环中调用无数次,否则对性能的影响可以忽略不计)
我强烈建议不要使用 raw-pointer- 和 CStr
-reference-solutions,它们是脚枪。
就个人而言,我会选择闭包,因为它实现了基本情况:访问字符串的代码的上下文必须移动到字符串所在的位置;我们不能让字符串移动到上下文 的位置(据我们所知,即使是调用者也可能无法控制)。
闭包解决方案应该让你既能吃蛋糕又能吃:类型 impl Fn(&CStr) -> T
的闭包可以是 |s| s.to_owned()
,如果需要,可以制作 with_string_from_host
return 的副本。