Is This Safe?
Suppose I wanted to call some Rust code from within Python. Suppose my lib.rs looks something like this:
#[no_mangle]
pub extern fn add(left: i32, right: i32) -> i32 {
return left+right;
}
And supposed I called this code from Python using ctypes like this:
import ctypes
def rust_add(left, right):
rust_lib = ctypes.CDLL("path/to/the/so/file")
return rust_lib.add(left, right)
Then is the above safe?
An Anticipated Snag: Integer Overflow
Overflow in the Inputs
One issue which even I - with my extremely tenuous grasp of Rust - can foresee is integer overflow. I imagine that, if either left or right was greater than 2^32, that would cause bad things to happen: either Rust would hit a run-time error (most likely), or it would just return silly answers (worst case). Would some type-checking in the Python, perhaps using NumPy's uint32, be sufficient to prevent this issue?
Overflow in the Workings
This one is more insidious: suppose I ask, from Python, my Rust function to add (2^32)-1 and (2^32)-2. There shouldn't be any immediate overflow, but there will be once we add the two numbers together. As I understand it, on hitting such an overflow, Rust will panic in debug mode but try its best to soldier on in release mode.
Summary
- Is there any well-known practice for smoothing out the difficulties that arise when passing integers between a dynamic-width language, such as Python, and a fixed-width one, such as Rust, other than thorough testing?
- Are there any issues with passing integers between Rust and Python apart from integer overflow?
By default,
ctypeswill convert integer parameters and function return values to it'sc_inttype, whose size is dependant on the platform. Values exceeding this size will be truncated. Ifc_inthappens to not be 32 bits, that would be lead to undefined behavior in the Rust sense (you end up calling the rust function with an incorrect calling convention). That is probably not what you would want in this case. (Admittedly,c_intis 32 bits on most common desktop plattforms).I still recommend the following python code:
This way,
ctypeswill convert arguments and return types to the correct integer type regardless of the plattform.It will still truncate larger integers and throw an exception for other python types though. If you want to have a different way of handling those cases you would have to do that yourself. For more details on how type conversions are handled by
ctypesI recommend their excellent documentation.If you just want to make sure that the passed in values are sane, and get some kind of exception in all other cases, you could write a simple function like this:
Regarding overflows on the Rust side: those will follow Rust's usual integer overflow rules (panic in debug, two's complement wrap in release). If that's not what you want, code some other logic.