2015-05-28 8 views
15

Sono molto nuovo a Rust. Come posso restituire un String da una funzione di Rust che può essere utilizzata in Python?Restituire una stringa dalla funzione Ruggine a Python

Qui è la mia realizzazione Rust:

use std::ffi::CString; 

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

E il codice Python che lo chiama:

from ctypes import cdll, c_char_p 

lib = cdll.LoadLibrary("target/release/libtest.so") 
result = lib.query() 

print(c_char_p(result).value) 

ottengo un errore di segmentazione quando la sua corsa.

EDIT: Usando il codice della ruggine di Vladimir Matveev di seguito sono riuscito a farlo funzionare con le modifiche al mio codice 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) 
+0

Rivedere http: // StackOverflow.it/questions/30440068/segmentation-fault-when-calling-a-rug-lib-with-ruby-ffi e http://stackoverflow.com/questions/30312885/pass-python-list-to-embedded-rust- funzione/30313295 # 30313295 e facci sapere come la tua domanda è diversa. – Shepmaster

+0

Ho esaminato entrambe le domande e sono diverse. Nella prima, la chiamata proviene da Ruby e la mia domanda proviene da Python. Nella seconda domanda, il valore restituito è un numero intero, che è un caso semplice. Qui, il ritorno è specificamente un valore stringa. – LeeMobile

+1

Non c'è assolutamente nulla di diverso sul lato Ruggine che dovrebbe cambiare a seconda della lingua da cui si sta chiamando. Per quanto riguarda il codice Rust, C lo sta chiamando. Ogni altra lingua chiama ciò che sembra codice C ad esso. – Shepmaster

risposta

11

La versione più diretta sarebbe questo:

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

Qui torniamo un puntatore a uno zero-terminated sequenza di char s che può essere passata a Python c_char_p. Non è possibile restituire solo CString perché è una struttura di Rust che non dovrebbe essere usata direttamente nel codice C - essa incorpora Vec<u8> e in realtà consiste in tre interi con il puntatore. Non è compatibile con C's char* direttamente. Dobbiamo ricavarne un puntatore grezzo. Il metodo CString::into_raw() fa questo: consuma il valore CString in base al valore, "dimentica", quindi la sua allocazione non verrà distrutta e restituisce un puntatore *mut c_char all'inizio dell'array.

Tuttavia, in questo modo la stringa verrà trapelata perché dimentichiamo la sua allocazione sul lato di Rust e non verrà mai liberata. Non conosco abbastanza FFI di Python, ma il modo più diretto per risolvere questo problema è creare due funzioni, una per la produzione dei dati e l'altra per liberarla. Allora avete bisogno di liberare i dati dal lato Python chiamando questa funzione liberazione:

// 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() metodo accetta un puntatore *mut c_char e crea un'istanza CString fuori di esso, calcolando la lunghezza del sottostante stringa terminata da zero in processi. Questa operazione implica il trasferimento della proprietà, quindi il valore CString risultante sarà proprietario dell'allocazione e, una volta rilasciato, l'allocazione verrà liberata. Questo è esattamente quello che vogliamo.

+0

Avevo bisogno di fare i primi due passi da [qui] (https://github.com/rust-lang/libc#usage) per farlo funzionare: (1) aggiungere dipendenza a rustc in 'Cargo.toml' e (2) importalo nel file ruggine dove è usato. (Non sono sicuro che sia sempre necessario, sono completamente nuovo per la ruggine.) – ArneHugo

1

Il problema qui è che si restituisce un CString direttamente, che non lo fa corrisponde alla rappresentazione di una stringa in C (è possibile vedere here il codice sorgente di CString).

Si dovrebbe restituire un puntatore alla stringa, utilizzando s.as_ptr(). Tuttavia, è necessario assicurarsi che la stringa non sia deallocata alla fine della funzione, poiché ciò comporterebbe un puntatore pendente.

L'unica soluzione a cui posso pensare è utilizzare forget per far sì che la ruggine dimentichi la variabile anziché liberarla. Ovviamente dovrai trovare un modo per liberare la stringa in seguito per evitare una perdita di memoria (vedi la risposta di Vladimir).

Con i cambiamenti che ho citato, il codice di ruggine dovrebbe essere il seguente:

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; 
} 
Problemi correlati