这安全吗?
假设我想从Python中调用一些Rust代码。假设我的看起来像这样:lib.rs
#[no_mangle]
pub extern fn add(left: i32, right: i32) -> i32 {
return left+right;
}
假设我使用ctypes
如下方式从 Python 调用这段代码:
import ctypes
def rust_add(left, right):
rust_lib = ctypes.CDLL("path/to/the/so/file")
return rust_lib.add(left, right)
那么上面的方法安全吗?
预期的障碍:整数溢出
输入溢出
即使是我(对 Rust 的掌握极其薄弱)也能预见到一个问题:整数溢出。我想,如果 或left
大于right
2^32,就会导致不好的事情发生:要么 Rust 会遇到运行时错误(最有可能),要么它只会返回愚蠢的答案(最坏的情况)。Python 中的一些类型检查(也许使用 NumPy 的)是否uint32
足以防止此问题?
工作中的溢出
这个更加阴险:假设我从 Python 中要求我的 Rust 函数添加 (2^32)-1 和 (2^32)-2。不应该立即发生溢出,但是一旦我们将两个数字加在一起就会发生溢出。据我了解,遇到这样的溢出时,Rust 在调试模式下会出现恐慌,但在发布模式下会尽力坚持下去。
概括
- 除了彻底的测试之外,是否有任何众所周知的实践可以解决在动态宽度语言(例如 Python)和固定宽度语言(例如 Rust)之间传递整数时出现的困难?
- 除了整数溢出之外,在 Rust 和 Python 之间传递整数是否存在任何问题?
默认情况下,
ctypes
会将整数参数和函数返回值转换为其c_int
类型,其大小取决于平台。超过此大小的值将被截断。如果c_int
碰巧不是 32 位,这将导致 Rust 意义上的未定义行为(您最终会使用不正确的调用约定来调用 rust 函数)。在这种情况下,这可能不是您想要的。(无可否认,在最常见的桌面平台上c_int
是32 位)。我还是推荐下面的python代码:
这样,
ctypes
无论平台如何,都会将参数和返回类型转换为正确的整数类型。但它仍然会截断较大的整数并引发其他 python 类型的异常。如果您想以不同的方式处理这些情况,您必须自己做。有关如何处理类型转换的更多详细信息,
ctypes
我推荐他们优秀的文档。如果您只是想确保传入的值是正常的,并在所有其他情况下得到某种异常,您可以编写一个简单的函数,如下所示:
关于 Rust 方面的溢出:这些将遵循 Rust 通常的 整数溢出规则(调试中的恐慌,发布中的补码换行)。如果这不是您想要的,请编写一些其他逻辑。