2015-05-19 5 views
5

Sto tentando di scrivere binding Rust per una libreria di raccolta C (Judy Arrays [1]) che fornisce solo spazio per memorizzare un valore di larghezza del puntatore. La mia azienda ha una discreta quantità di codice esistente che utilizza questo spazio per memorizzare direttamente valori non puntatore come interi di larghezza del puntatore e piccole strutture. Mi piacerebbe che i miei binding Rust consentissero l'accesso sicuro al tipo a tali raccolte utilizzando i generici, ma sto avendo problemi a far funzionare correttamente la semantica del puntatore.Generici di puntatori tramite `mem :: transmute()`

La funzione mem::transmute() sembra uno strumento potenziale per implementare il comportamento desiderato, ma il tentativo di utilizzarlo su un'istanza di un tipo parametrizzato genera un errore di compilazione confuso.

codice

Esempio:

pub struct Example<T> { 
    v: usize, 
    t: PhantomData<T>, 
} 

impl<T> Example<T> { 
    pub fn new() -> Example<T> { 
     Example { v: 0, t: PhantomData } 
    } 

    pub fn insert(&mut self, val: T) { 
     unsafe { 
      self.v = mem::transmute(val); 
     } 
    } 
} 

errore risultante:

src/lib.rs:95:22: 95:36 error: cannot transmute to or from a type that contains type parameters in its interior [E0139] 
src/lib.rs:95    self.v = mem::transmute(val); 
            ^~~~~~~~~~~~~~ 

Significa questo tipo costituito solo di un parametro "contiene parametri di tipo al suo interno" e quindi transmute() semplicemente non funzionerà Qui? Qualche suggerimento sul modo giusto di farlo?

(Related question, nel tentativo di ottenere lo stesso risultato, ma non necessariamente via mem::transmute().)

[1] Sono a conoscenza del progetto esistente rust-judy, ma non supporta il puntatore-stashing voglio e sto scrivendo questi nuovi attacchi in gran parte come un esercizio di apprendimento comunque.

+1

Modificato alla prima domanda; creerà nuovo e link per il secondo. – llasram

+2

Una domanda interessante! È quasi come se si volesse scrivere "dove sizeof (T) == sizeof (usize)". – Shepmaster

+0

@Shepmaster: Questo sarebbe complicato, perché il collegamento tra la clausola 'where' e' transmute' sarebbe leggermente difficile da stabilire (specialmente se è possibile utilizzare vincoli più generali, come con '<=' and '> ='). D'altra parte, mi ha ricordato: [RFC: Coercible e HasPrefix per Zero Cost Conversions] (https://github.com/rust-lang/rfcs/issues/270); il vincolo potrebbe essere applicabile per un 'Transmable dove mem :: size_of :: () == mem :: size_of :: ()' e quindi 'transmute' da' T' a 'usize' richiederebbe' T: Transmutable '=> perfettamente decidibile. –

risposta

2

Invece di trasmutare T a usize direttamente, è possibile trasformare un &T-&usize:

pub fn insert(&mut self, val: T) { 
    unsafe { 
     let usize_ref: &usize = mem::transmute(&val); 
     self.v = *usize_ref; 
    } 
} 

Attenzione che questo può leggere da una posizione di memoria non valida se la dimensione del T è più piccola rispetto alla dimensione del usize o se i requisiti di allineamento sono diversi. Ciò potrebbe causare un segfault. È possibile aggiungere un'asserzione per impedire ciò:

assert_eq!(mem::size_of::<T>(), mem::size_of::<usize>()); 
assert!(mem::align_of::<usize>() <= mem::align_of::<T>()); 
+0

Il mio obiettivo è quello di applicare la compatibilità delle dimensioni in fase di compilazione, poiché 'transmute()' funziona su valori nudi. La trasmissione dei riferimenti e l'aggiunta dei controlli di runtime vanificano lo scopo. – llasram

+0

Ora che ho più esperienza di Rust, sono d'accordo che questa è la strada da percorrere per ora. Le asserzioni nei metodi di costruzione possono garantire che il codice sia sicuro; la compatibilità delle dimensioni non può essere verificata al momento della compilazione. – llasram

+0

Rileggendolo nuovamente dopo il tuo commento, credo che tu debba anche verificare i requisiti di allineamento dei tipi. Ho aggiornato la risposta di conseguenza. – Ruud

Problemi correlati