我现在已经学习Rust大约两个星期了,今天我进入了它的FFI。我使用Python来使用Rust,使用ctypes和libc。我传递了整数,字符串,甚至学会了传递整数列表(thanks to this wonderful answer)。
然后,我尝试传递一个字符串列表(遵循该答案背后的原因),但我失败了,因为我无法获得领先。在Python中,我有类似的东西来传递字符串数组。
def testRust():
lib = ctypes.cdll.LoadLibrary(rustLib)
list_to_send = ['blah', 'blah', 'blah', 'blah']
c_array = (ctypes.c_char_p * len(list_to_send))()
lib.get_strings(c_array, len(list_to_send))
在Rust中,我认为应该有一些东西(比如STRING_RECEIVER
)来收集传入的字符串,但我找不到。
#![feature(libc)]
extern crate libc;
use std::slice;
use libc::{size_t, STRING_RECEIVER};
#[no_mangle]
pub extern fn get_strings(array: *const STRING_RECEIVER, length: size_t) {
let values = unsafe { slice::from_raw_parts(array, length as usize) };
println!("{:?}", values);
}
有没有其他方法可以实现这一目标?
答案 0 :(得分:12)
数字数组的情况绝对没有区别。 C字符串是以零结尾的字节数组,因此它们在Rust中的表示形式为*const c_char
,然后可以将其转换为&CStr
,然后可以使用它来获取&[u8]
然后&str
1}}。
的Python:
import ctypes
rustLib = "libtest.dylib"
def testRust():
lib = ctypes.cdll.LoadLibrary(rustLib)
list_to_send = ['blah', 'blah', 'blah', 'blah']
c_array = (ctypes.c_char_p * len(list_to_send))(*list_to_send)
lib.get_strings(c_array, len(list_to_send))
if __name__=="__main__":
testRust()
锈:
#![feature(libc)]
extern crate libc;
use std::slice;
use std::ffi::CStr;
use std::str;
use libc::{size_t, c_char};
#[no_mangle]
pub extern fn get_strings(array: *const *const c_char, length: size_t) {
let values = unsafe { slice::from_raw_parts(array, length as usize) };
let strs: Vec<&str> = values.iter()
.map(|&p| unsafe { CStr::from_ptr(p) }) // iterator of &CStr
.map(|cs| cs.to_bytes()) // iterator of &[u8]
.map(|bs| str::from_utf8(bs).unwrap()) // iterator of &str
.collect();
println!("{:?}", strs);
}
运行:
% rustc --crate-type=dylib test.rs
% python test.py
["blah", "blah", "blah", "blah"]
同样,你应该小心一生,并确保Vec<&str>
不会超过Python方面的原始值。