python调用rust,将字符串列表从Python传递到Rust

I've been learning Rust for about two weeks now and today, I got into its FFI. I used Python to play with Rust, using ctypes and libc. I passed integers, strings and even learned to pass a list of integers (thanks to this wonderful answer).

Then, I tried to pass a list of strings (following the reasoning behind the that answer), but I failed, as I couldn't get a lead on it. In Python, I have something like this to pass the array of strings.

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))

In Rust, I thought that there should be something (like a STRING_RECEIVER) to collect the incoming strings, but I can't find one.

#![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);

}

Is there any alternative way to achieve this?

解决方案

There is absolutely no difference with the case of array of numbers. C strings are zero-terminated arrays of bytes, so their representation in Rust will be *const c_char, which could then be converted to &CStr which then can be used to obtain &[u8] and then &str.

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()

Rust:

#![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 = 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);

}

Running:

% rustc --crate-type=dylib test.rs

% python test.py

["blah", "blah", "blah", "blah"]

And again, you should be careful with lifetimes and ensure that Vec does not outlive the original value on the Python side.

你可能感兴趣的:(python调用rust)