2017-02-08 26 views
6

Mam cechę, w której chcę zapewnić metodę. Metodę tę należy wdrożyć w kategoriach niektórych pomocników, którzy nie mają żadnego biznesu w obrębie cechy i nie są wystarczająco banalni, aby dynamiczny polimorfizm miał więcej sensu niż uczynienie ich ogólnymi. Więc mam kod wzdłuż liniiDostarczona metoda odlewania i własna cecha obiektu

fn use_trait(x: &Trait) { 
    println!("object says {}", x.needed()); 
} 

trait Trait { 
    fn needed(&self) -> &str; 

    fn provided(&self) { 
     use_trait(self); 
    } 
} 

struct Struct(); 

impl Trait for Struct { 
    fn needed(&self) -> &str { 
     "Hello, world!" 
    } 
} 

fn main() { 
    Struct().provided(); 
} 

które jednak does not compile, z błędem:

error[E0277]: the trait bound `Self: std::marker::Sized` is not satisfied 
--> <anon>:9:19 
    | 
9 |   use_trait(self); 
    |     ^^^^ the trait `std::marker::Sized` is not implemented for `Self` 
    | 
    = help: consider adding a `where Self: std::marker::Sized` bound 
    = note: required for the cast to the object type `Trait` 

rozumiem dlaczego-to nie jest gwarantowana ktoś nie będzie stanowić cechę dla typu niezagruntowanej (konwersja z &T where T: Trait na &Trait wymaga T: Sized, ale deklaracja tego nie wymaga).

Jednak porady nie zrobią tego, czego potrzebuję. Mogę dodać

fn needed(&self) -> &str where Self: Sized 

ale wtedy metoda needed() nie będą dostępne na &Trait (bo Trait : ?Sized), co sprawia, że ​​rzeczy bezużyteczne, ponieważ typ (rzeczywisty, który robi coś pożytecznego) jest zawsze traktowane jako Arc<Trait>. I dodając

trait Trait: Sized 

jest jeszcze gorzej, bo nie pozwala &Trait na wszystkich (Trait jako typ jest nieklejony, więc Trait typ robi nie wdrożyć cechę Trait).

Oczywiście mogę po prostu zrobić

fn use_trait<T: Trait>(x: &T) 

ale jest dużo za nim w realnym kodzie, więc nie chcę monomorphisation tam zwłaszcza, że ​​cecha jest inaczej zawsze traktowane jako cecha obiektu.

Czy istnieje sposób, aby powiedzieć Rustowi, że wszystkie typy, które mają rozmiar impl Trait, muszą być zwymiarowane i czy istnieje definicja metody, która powinna działać dla nich wszystkich?

+0

Zobacz również [Konwersja obiektu Rust Trait] (http://stackoverflow.com/q/41604107/155423) – Shepmaster

Odpowiedz

0

Ulepszona wersja @JoshuaEntrekin's answer:

Pomocnik as_trait funkcji można umieścić w dodatkowym cechą który dostaje realizację koc dla wszystkich typów Sized próbujących realizować Trait. Następnie implementator Trait nie musi robić nic specjalnego, a konwersja działa.

fn use_trait(x: &Trait) { 
    println!("object says {}", x.needed()); 
} 

trait Trait : AsTrait { 
    fn needed(&self) -> &str; 

    fn provided(&self) where Self : AsTrait { 
     use_trait(self.as_trait()); 
    } 
} 

trait AsTrait { 
    fn as_trait(&self) -> &Trait; 
} 

impl<T : Trait + Sized> AsTrait for T { 
    fn as_trait(&self) -> &Trait { self } 
} 

struct Struct(); 

impl Trait for Struct { 
    fn needed(&self) -> &str { 
     "Hello, world!" 
    } 
} 

fn main() { 
    Struct().provided(); 
} 

(na play).

Możliwe byłoby również po prostu wstawienie provided w cechę pomocniczą, ale wówczas musiałaby ona niepotrzebnie przesyłać dynamicznie do innych metod o numerach Self.


Aktualizacja: Faktycznie, chodzi o to, że w dalszym ciągu powinno być możliwe, aby zastąpić provided.

Teraz powyższe można jeszcze poprawić, czyniąc go ogólnym. Jest std::makrer::Unsize, który jest niestabilny w momencie pisania tego tekstu. Nie możemy

trait Trait : Unsize<Trait> 

ponieważ Rust nie pozwala CRTP, ale na szczęście to wystarczy, aby umieścić ograniczenie na metodzie. Więc

fn use_trait(x: &Trait) { 
    println!("object says {}", x.needed()); 
} 

trait Trait { 
    fn needed(&self) -> &str; 

    fn provided(&self) where Self: AsObj<Trait> { 
     use_trait(self.as_obj()); 
    } 
} 

trait AsObj<Tr: ?Sized> { 
    fn as_obj(&self) -> &Trait; 
} 

// For &'a Type for Sized Type 
impl<Type: Trait> AsObj<Trait> for Type { 
    fn as_obj(&self) -> &Trait { self } 
} 

// For trait objects 
impl AsObj<Trait> for Trait { 
    fn as_obj(&self) -> &Trait { self } 
} 

struct Struct(); 

impl Trait for Struct { 
    fn needed(&self) -> &str { 
     "Hello, world!" 
    } 

    fn provided(&self) { 
     println!("Aber dieses Objekt sagt Grüß Gott, Welt!"); // pardon my German, it is rusty. 
    } 
} 

fn main() { 
    let s: &Trait = &Struct(); 
    s.provided(); 
} 

(na play)

To w końcu sprawia, że ​​jest przezroczysty dla implementors innych wersjach.

Zobacz także this users thread.

2

potrzeba dodatkowego as_trait funkcję Trait i jego implementacje:

trait Trait { 
    fn needed(&self) -> &str; 

    fn provided(&self) { 
     use_trait(self.as_trait()); 
    } 

    fn as_trait(&self) -> &Trait; 
} 

struct Struct(); 

impl Trait for Struct { 
    fn needed(&self) -> &str { 
     "Hello, world!" 
    } 

    fn as_trait(&self) -> &Trait { 
     self as &Trait 
    } 
} 

Można spróbować go na boisku. (trait objects)

+0

Obiekty cech typu "niezaszyfrowany typ" mają rozmiar; dwa 'usizes'. Jego własna cecha, bez obiektu, jest typem niezaszyfrowanym. –

+0

Czy możesz wyjaśnić, dlaczego potrzebna jest metoda 'as_trait()', a parametr 'use_trait (self as & Trait)' nie działa bezpośrednio w 'provided()'? –

+0

@ChrisEmerson, Dodam wyjaśnienie do samego pytania. Jest dość oczywiste, dlaczego nie działa w prosty sposób. –