2015-03-09 14 views
5

Sto tentando di utilizzare una funzione di callback su un oggetto tratto. Ho ridotto il mio problema al seguente codice (playpen):Utilizzo di callback su oggetti tratto

trait Caller { 
    fn call(&self, call: fn(&Caller)) where Self: Sized { 
     call(self) 
    } 
} 

struct Type; 
impl Caller for Type {} 

fn callme(_: &Caller) {} 

fn main() { 
    let caller: Box<Caller> = Box::new(Type); 
    caller.call(callme); // does not work 
    //callme(&*caller); // works 
} 

che si traduce in

<anon>:14:12: 14:24 error: the trait `core::marker::Sized` is not implemented for the type `Caller` [E0277] 
<anon>:14  caller.call(callme); // does not work 
        ^~~~~~~~~~~~ 

Aggiunta di un Sized destinata a Caller risultati in:

<anon>:3:14: 3:18 error: cannot convert to a trait object because trait `Caller` is not object-safe [E0038] 

davvero non lo so capire perché ho bisogno del limite Sized sul tratto. Funnily funziona se uso direttamente il callback. Come faccio a far funzionare questo?

Edit: Grazie per la risposta che la società si avvicinò con una bella soluzione

trait Caller { 
    fn borrow(&self) -> &Caller; 
    fn call(&self, call: fn(&Caller)) { 
     call(self.borrow()) 
    } 
} 

struct Type; 
impl Caller for Type { 
    fn borrow(&self) -> &Caller { self } 
} 

fn callme(_: &Caller) {} 

fn main() { 
    let caller: Box<Caller> = Box::new(Type); 
    caller.call(callme); 
} 

risposta

3

L'argomento call in fn call prende un oggetto tratto &Caller, così definendolo richiede costringere il self di riferimento (di tipo &Self) a un oggetto tratto &Caller. La coercizione è possibile solo quando &Self è un puntatore sottile anziché un puntatore grasso come un oggetto tratto o una fetta &[T]. &Self è un puntatore sottile esattamente quando Self: Sized. Il compilatore ha come valore predefinito Self in caratteri non compresi tra Sized e pertanto è necessaria la restrizione aggiuntiva. Lo Sized trait indica che il tipo ha una dimensione nota al momento della compilazione, non è necessario memorizzare informazioni aggiuntive (accanto al puntatore, rendendolo "grasso") per calcolarlo in fase di runtime.

Purtroppo, questo lascia un foro: AFAIK, non è infatti possibile avere un tale metodo sia un metodo predefinito e comunque in grado di chiamare su oggetti tratto, poiché un oggetto tratto &Caller ha Self = Caller che non è Sized. Tuttavia, dovrebbe funzionare se il metodo è implementato manualmente per ogni tipo:

trait Caller { 
    fn call(&self, call: fn(&Caller)); 
} 

struct Type; 
impl Caller for Type { 
    fn call(&self, call: fn(&Caller)) { 
     call(self) 
    } 
} 

fn callme(_: &Caller) {} 

fn main() { 
    let caller: Box<Caller> = Box::new(Type); 
    caller.call(callme); 
} 

La dichiarazione call metodo nel tratto non ha più bisogno il where Self: Sized poiché non sta cercando di fare coercizione oggetto tratto stesso, e la le implementazioni concrete hanno un controllo molto maggiore su come si ottiene l'oggetto tratto &Caller. Per i tipi Sized, funziona direttamente, come il codice originale where Self: Sized.

+0

Una cosa non mi è ancora chiara dopo aver letto questo: c'è un modo per far funzionare il codice dell'OP? – Shepmaster

+1

Err, sì, ho dimenticato di rispondere effettivamente alla domanda. :) – huon

+0

Grazie, ma quello che non capisco è il motivo per cui non è possibile trasformare & Self to & Caller. Non dovrebbe essere solo una trasformazione dell'identità come & Self è già un & Caller? Edit: La risposta è probabilmente che & Self non è & Caller ma potrebbe essere qualcos'altro ma sembra che dovrebbe essere risolvibile in rustc. – Ferio

Problemi correlati