weapons rustlabs revolver pistol labs l96 python rust

python - pistol - rustlabs revolver



Devolviendo una cadena desde la funciĆ³n Rust a Python (2)

El problema aquí es que está devolviendo un CString directamente, que no corresponde a la representación de una cadena en C (puede ver here el código fuente de CString ).

Debería devolver un puntero a la cadena, usando s.as_ptr() . Sin embargo, debe asegurarse de que la cadena no esté desasignada al final de la función, ya que eso daría como resultado un puntero colgante.

La única solución que se me ocurre es usar forget para dejar que el óxido olvide la variable en lugar de liberarla. Por supuesto, necesitará encontrar una forma de liberar la cadena más tarde para evitar una pérdida de memoria (vea la respuesta de Vladimir).

Con los cambios que mencioné, su código Rust debería ser el siguiente:

use std::ffi::CString; use std::mem; #[no_mangle] pub extern fn query() -> *const i8 { let s = CString::new("Hello!").unwrap(); let ptr = s.as_ptr(); mem::forget(s); return ptr; }

Soy muy nuevo en Rust. ¿Cómo devolvería un String desde una función de Rust que se puede usar en Python?

Aquí está mi implementación de Rust:

use std::ffi::CString; #[no_mangle] pub extern fn query() -> CString { let s = CString::new("Hello!").unwrap(); return s; }

Y el código de Python que lo llama:

from ctypes import cdll, c_char_p lib = cdll.LoadLibrary("target/release/libtest.so") result = lib.query() print(c_char_p(result).value)

Me sale una falla de segmentación cuando se ejecuta.

EDITAR: Usando el código Rust de Vladimir Matveev a continuación, pude hacer que funcionara con los cambios en mi código de python:

from ctypes import * lib = cdll.LoadLibrary("target/release/libtest.so") lib.query.restype = c_char_p result = lib.query() print cast(result, c_char_p).value lib.free_query(result)


La versión más directa sería esta:

use libc::c_char; use std::ffi::CString; use std::mem; #[no_mangle] pub extern fn query() -> *mut c_char { let s = CString::new("Hello!").unwrap(); s.into_raw() }

Aquí devolvemos un puntero a una secuencia terminada en cero de caracteres que se puede pasar a c_char_p de Python. No puede devolver solo CString porque es una estructura de Rust que no se debe usar en el código C directamente; envuelve Vec<u8> y en realidad consta de tres enteros del tamaño de un puntero. No es compatible con C''s char* directamente. Necesitamos obtener un puntero crudo fuera de él. CString::into_raw() método CString::into_raw() hace esto: consume CString por valor, "lo olvida" para que su asignación no se destruya y devuelve un puntero *mut c_char al principio de la matriz.

Sin embargo, de esta manera se filtrará la cadena porque olvidamos su asignación en el lado Rust y nunca se liberará. No conozco la FFI de Python lo suficiente, pero la forma más directa de solucionar este problema es crear dos funciones, una para generar los datos y otra para liberarlos. Entonces necesitas liberar los datos del lado de Python llamando a esta función de liberación:

// above function #[no_mangle] pub extern fn query() -> *mut c_char { ... } #[no_mangle] pub extern fn free_query(c: *mut c_char) { // convert the pointer back to `CString` // it will be automatically dropped immediately unsafe { CString::from_raw(c); } }

CString::from_raw() método CString::from_raw() acepta un puntero *mut c_char y crea una instancia CString partir de él, calculando la longitud de la cadena subyacente terminada en cero en el proceso. Esta operación implica la transferencia de propiedad, por lo que el valor CString resultante será el propietario de la asignación y, cuando se elimina, la asignación se libera. Esto es exactamente lo que queremos.