8 个版本
0.2.0 | 2023年9月28日 |
---|---|
0.1.6 | 2023年8月10日 |
#242 in 编程语言
每月 1,285 次下载
41KB
469 行
cee-scape
cee-scape 包通过一个确保 LLVM 不会错误编译的接口,为 Rust 提供对 setjmp
和 sigsetjmp
功能的访问
示例
你可能想象你有一些类似这样的 C 代码(极度简化)
#include <setjmp.h>
#include <stdint.h>
uint32_t subtract_but_longjmp_if_underflow(jmp_buf env, uint32_t a, uint32_t b) {
if (b > a) {
longjmp(env, b - a);
}
return a - b;
}
如果你想在 Rust 中调用这些 C 代码,你需要建立一个跳转环境(jmp_buf env
参数);但是 Rust 并不提供 setjmp
。
以下是使用此包来解决你的问题的方法
use cee_scape::call_with_setjmp;
// This invocation passes parameters that follow normal control flow.
assert_eq!(call_with_setjmp(|env| {
(unsafe {
subtract_but_longjmp_if_underflow(env, 10, 3)
}) as c_int
}), 7);
// This invocation passes parameters that cause a non-local jump.
assert_eq!(call_with_setjmp(|env| {
unsafe {
subtract_but_longjmp_if_underflow(env, 3, 10);
panic!("should never get here.");
}
}), 7);
为什么不在函数中直接添加 setjmp
呢?
在 Rust 项目中,关于这个问题的讨论已经相当广泛。
特别是请参阅 rust-lang/libc PR 1216 和 rust-lang/rfcs Issue 2625。
答案是多方面的。
首先,要添加对调用 setjmp
的支持,就需要在 Rust 编译器中添加额外的支持。具体来说,需要支持将函数标记为可能多次返回,例如通过在 rust-lang/rfcs Issue 2625 中讨论的 returns_twice
属性。
其次,对 setjmp
函数的调用将以奇特的方式与 Rust 借用检查器交互;除非借用检查器被扩展以理解 returns_twice
属性的意义,否则会引发立即的不稳定行为,例如将同一个栈槽中的所有权对象多次移动。这可能会导致相同的值被丢弃多次(这将是不可靠的)。
第三,根据 C 标准,setjmp
函数本身只在非常具体的上下文中具有定义良好的行为(再次在 rust-lang/rfcs Issue 2625 中讨论);甚至在 Rust 中,let x = setjmp(...)
都将是未定义的行为。
cee-scape 提供的方法,如 call_with_setjmp
,通过限制 setjmp
的使用范围到特定的编码模式,从而绕过了上述问题
call_with_setjmp(|env| { ... })
在调用 call_with_setjmp
的动态作用域内,可以正常返回,或者通过 longjmp
跳转到给定的跳转环境 env
(这将导致从 call_with_setjmp
调用返回)。无论哪种方式,外部调用最多只返回一次。给定的跳转环境仅在动态作用域内可用(Rust 的生命周期规则有助于强制执行此约束)。
为什么叫 cee-scape
这是一个双关语:C 的跳转环境也被称为 "escape continuations"。这个包实现了 C 的逃离。
依赖项
~225KB