2015-03-09 14 views
5

Próbuję użyć funkcji wywołania zwrotnego na obiekcie cechy. I zmniejszona mój problem z następującym kodem (playpen):Korzystanie z wywołań zwrotnych na obiektach obiektów

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 
} 

co skutkuje

<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 
        ^~~~~~~~~~~~ 

Dodawanie Sized związany Caller wyniki w:

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

Naprawdę nie wiem zrozum, dlaczego potrzebuję Sized związanej z cechą. Funnily to działa, jeśli używam wywołania zwrotnego bezpośrednio. Jak mogę to uruchomić?

Edit: Dzięki odpowiedź I teraz pojawił się z miłym rozwiązania

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

Odpowiedz

3

argument call w fn call zajmuje obiekt cechę &Caller, więc nazywając go wymaga zmuszanie odniesienia (typu &Self) self do obiekt cechy &Caller. Przymus jest możliwy tylko wtedy, gdy &Self jest cienkim wskaźnikiem, a nie grubym wskaźnikiem, takim jak obiekt cech lub wycinek &[T]. &Self to cienki wskaźnik dokładnie wtedy, gdy Self: Sized. Domyślnie kompilator ma wartość Self, a jego cechy nie są Sized, więc dodatkowe ograniczenie jest wymagane. Model Sized trait oznacza, że ​​typ ma rozmiar znany w czasie kompilacji, nie ma potrzeby przechowywania dodatkowych informacji (obok wskaźnika, co czyni go "grubym") do obliczania go w czasie wykonywania.

Niestety, pozostawia to dziura: AFAIK, nie jest to faktycznie możliwe, że taka metoda jest metodą domyślną i nadal będą mogli nazwać na obiektach cecha, ponieważ obiekt cecha &Caller ma Self = Caller który nie jest Sized. Należy jednak działać, jeśli metoda jest realizowana ręcznie dla każdego typu:

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

Deklaracja call metoda w cechę już nie potrzebuje where Self: Sized ponieważ nie próbuje zrobić sam przymus cecha obiektu, a konkretne implementacje mają znacznie większą kontrolę nad sposobem uzyskania obiektu cechy &Caller. Dla typów Sized działa bezpośrednio, tak jak oryginalny kod where Self: Sized.

+0

Jedna rzecz, której wciąż nie jestem pewien po przeczytaniu tego - czy istnieje sposób na sprawienie, aby kod OP działał? – Shepmaster

+1

Err, tak, zapomniałem odpowiedzieć na pytanie. :) – huon

+0

Dziękuję, ale nie rozumiem, dlaczego nie można przekształcić & Self na & Caller. Czy nie powinna to być tylko transformacja tożsamości, ponieważ & Self jest już & Caller? Edycja: Odpowiedź jest prawdopodobnie taka, że ​​& Self nie jest & Caller, ale może być czymś innym, ale brzmi jak powinno być naprawiane w rustc. – Ferio

Powiązane problemy