una tuplas metodos listas lista funciones funcion definir comprensión anidadas python rust ctypes

tuplas - Pase la lista de Python a la función de Rust incrustada



listas anidadas en python (2)

Estoy aprendiendo cómo incorporar funciones de Rust en Python, y todo funciona bien si mis entradas son int s, pero no lista.

Si mi archivo lib.rs es:

#[no_mangle] pub extern fn my_func(x: i32, y: i32) -> i32 { return x + y; }

Puedo usar esto de la siguiente manera:

In [1]: from ctypes import cdll In [2]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so") In [3]: lib.my_func(5,6) Out[3]: 11

Sin embargo, si cambio mi lib.rs por:

#[no_mangle] pub extern fn my_func(my_vec: Vec<i32>) -> i32 { let mut my_sum = 0; for i in my_vec { my_sum += i; } return my_sum; }

Ya no puedo usarlo en Python (esta multa compilada):

In [1]: from ctypes import cdll In [2]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so") In [3]: lib.my_func([2,3,4]) --------------------------------------------------------------------------- ArgumentError Traceback (most recent call last) <ipython-input-3-454ffc5ba9dd> in <module>() ----> 1 lib.my_func([2,3,4]) ArgumentError: argument 1: <type ''exceptions.TypeError''>: Don''t know how to convert parameter 1

La razón, pensé que esto podría funcionar es que la list de Python y el Vec de Rust son las dos matrices dinámicas , pero aparentemente me falta algo aquí ...

¿Por qué mi intento no funciona? ¿Qué debo hacer para arreglarlo?


ctypes trata de enlaces C y en C no existe una matriz dinámica.

El objeto más cercano que puede pasar a una función C es un puntero a entero que, sin embargo, no es una matriz dinámica porque

  1. No lleva la información de tamaño
  2. No puedes hacer crecer o reducir el área, solo acceder a los elementos existentes

Una alternativa simple para pasar punteros (y ser muy cuidadoso al no superar el tamaño) que podría usar en su lugar es una API basada en función.

Por ejemplo:

getNumberOfThings() -> number getThing(index) -> thing

pero el código de Python sería como

def func(): n = getNumberOfThings() return [getThing(i) for i in range(n)]

La contraparte (que pasa una cantidad variable de elementos) sería

def func2(L): setNumberOfThings(len(L)) for i, x in enumerate(L): setThing(i, x)


No hagas esto:

#[no_mangle] pub extern fn my_func(my_vec: Vec<i32>) -> i32 { ... }

Básicamente, nunca desea aceptar o devolver un objeto Rust arbitrario en una función extern , solo los que son Repr . En su lugar, debe aceptar algo que sea representable por C. Como dice 6502 , la mejor idea para este caso particular sería aceptar un puntero y una longitud.

El Vec de Rust es conceptualmente un indicador de datos, un recuento y una capacidad . Puede modificar un Vec agregando o eliminando objetos, lo que puede provocar la reasignación. Esto es doblemente malo porque es probable que Python y Rust usen diferentes asignadores que no son compatibles entre sí. Segfaults miente de esta manera! Realmente quieres un pedazo .

En su lugar, haz algo así en el lado Rust:

extern crate libc; use libc::{size_t,int32_t}; use std::slice; #[no_mangle] pub extern fn my_func(data: *const int32_t, length: size_t) -> int32_t { let nums = unsafe { slice::from_raw_parts(data, length as usize) }; nums.iter().fold(0, |acc, i| acc + i) }

A saber, está utilizando los tipos de C garantizados para combinar y luego convierte el puntero y la longitud en algo que Rust sabe cómo manejar.

No soy Pythonista, pero este código improvisado (con la ayuda de ¿Cómo convierto una lista de Python en una matriz C usando ctypes? ) Parece funcionar con el óxido que tengo arriba:

import ctypes lib = ctypes.cdll.LoadLibrary("./target/debug/libpython.dylib") lib.my_func.argtypes = (ctypes.POINTER(ctypes.c_int32), ctypes.c_size_t) list_to_sum = [1,2,3,4] c_array = (ctypes.c_int32 * len(list_to_sum))(*list_to_sum) print lib.my_func(c_array, len(list_to_sum))

Por supuesto, es probable que desee ajustar eso para que sea más agradable para la persona que llama de su código.