2015-06-26 20 views
7

Dato che è la prima volta che imparo programmi di programmazione, sto attraversando un periodo difficile che mi avvolge le regole. Ora, mi sono confuso su perdite di memoria. Consideriamo un esempio. Dì, Rust sta lanciando un puntatore (a una corda) che Python prenderà.Come fermare la perdita di memoria quando si usa `as_ptr()`?

A Rust, (io sono solo l'invio del puntatore del CString)

use std::ffi::CString; 

pub extern fn do_something() -> *const c_char { 
    CString::new(some_string).unwrap().as_ptr() 
} 

In Python, (sto dereferenziazione il puntatore)

def call_rust(): 
    lib = ctypes.cdll.LoadLibrary(rustLib) 
    lib.do_something.restype = ctypes.c_void_p 
    c_pointer = lib.do_something() 
    some_string = ctypes.c_char_p(c_pointer).value 

Ora, la mia domanda è di circa liberare la memoria. Ho pensato che dovrebbe essere liberato in Python, ma poi la proprietà si apre. Perché, as_ptr sembra prendere un riferimento immutabile. Quindi, mi sono confuso se avrei dovuto liberare la memoria in Rust o Python (o entrambi?). Se sarà Rust, come dovrei liberare quando il flusso di controllo è tornato in Python?

+1

Si noti che in questo caso, ** c'è ** perdita di memoria. Invece, si tratta di un errore use-after-free, che di solito è molto più distruttivo. – Shepmaster

risposta

7

La funzione Rust do_something costruisce una temporanea CString, prende un puntatore in esso, e quindi cadere la CString. *const c_char non è valido dal momento in cui viene restituito. Se si è di notte, probabilmente si desidera CString#into_ptr anziché CString#as_ptr, in quanto il primo consuma lo CString senza deallocare la memoria. In stalla, è possibile mem::forget il CString. Quindi puoi preoccuparti di chi dovrebbe liberarlo.

Liberare da Python sarà complicato o impossibile, poiché Rust potrebbe utilizzare un allocatore diverso. L'approccio migliore sarebbe esporre una funzione Rust che prende un puntatore c_char, crea un CString per quel puntatore (anziché copiare i dati in una nuova allocazione) e lo elimina. Sfortunatamente la parte centrale (creazione dello CString) sembra impossibile per ora stabile: CString::from_ptr non è stabile.

Una soluzione sarebbe passare (un puntatore a) l'intera CString di Python e fornire una funzione di accesso per ottenere il puntatore char da esso. Hai semplicemente bisogno di boxare il CString e trasmutare la scatola a un puntatore raw. Quindi puoi avere un'altra funzione che trasmette il puntatore a una casella e lo lascia cadere.

+0

'CString' è rappresentato come' Box <[u8]> '; 'std :: raw :: Slice' (unstable) mostra la rappresentazione di quel tipo. Trasmutare avanti e indietro è probabilmente il modo migliore se vuoi trasferire la proprietà. –

+0

@ChrisMorgan 'CString' è diventato un' Box <[u8]> 'allo stesso tempo ho aggiunto' into_ptr'; prima, era un 'Vec '. – Shepmaster

+0

@ChrisMorgan Questo non è documentato, e diversamente dalla 'struct Box (* mut T)' non è ovvio che questo non cambierà mai. Quindi sono stanco di raccomandarlo, specialmente dal momento che una stable 'into_ptr' /' from_ptr' è probabilmente solo a due release di distanza. – delnan