2017-02-08 18 views
6

У меня есть черта, в которой я хочу предоставить метод. Этот метод должен быть реализован в терминах некоторых помощников, которые не имеют бизнеса внутри черты и достаточно нетривиальны, что динамический полиморфизм имеет больше смысла, чем их общий характер. Поэтому у меня есть код вдоль линийПредоставленный метод casting & self to trait object

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

который, однако, does not compile с ошибкой:

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` 

Я понимаю, почему-то не гарантируется, кто не будет выполнять признак для некалиброванного типа (преобразование с &T where T: Trait в &Trait требует T: Sized, но декларация этого не требует).

Однако совет не будет делать то, что мне нужно. Я могу добавить

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

но тогда needed() метод не будет доступен на &Trait (потому что Trait : ?Sized), что делает вещь бесполезная, так как тип (фактический один, что делает что-то полезное) является всегда обрабатывается как Arc<Trait>. А добавление

trait Trait: Sized 

еще хуже, потому что не позволяет &Trait вообще (Trait как тип несортированный, так Trait типа делает не реализации признака Trait).

Конечно, я могу просто сделать

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

но есть много за ним в реальном коде, так что я не хочу monomorphisation там особенно потому, что черта иначе всегда обрабатываются как признак объект.

Есть ли способ рассказать Ржавчине, что все типы, которые должны иметь размер impl Trait, и вот определение метода, который должен работать для всех из них?

+0

См. Также [Преобразование объекта Rust Trait] (http://stackoverflow.com/q/41604107/155423) – Shepmaster

ответ

0

Улучшенная версия @JoshuaEntrekin's answer:

Помощник as_trait функция может быть введен в качестве вспомогательного признака, который получает реализацию одеяло для всех типов Sized пытаются реализовать Trait. Тогда исполнитель Trait не должен ничего делать, а преобразование работает.

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

(на play).

Можно было бы просто поместить provided во вспомогательную черту, но тогда ему пришлось бы динамически отправлять другим методам Self без необходимости.


Update: На самом деле, дело в том, что она должна еще иметь возможность переопределить provided.

Теперь вышеуказанное можно улучшить, сделав его общим. Существует std::makrer::Unsize, который нестабилен на момент написания этой статьи. Мы не можем сделать

trait Trait : Unsize<Trait> 

, потому что ржавчина не позволяет CRTP, но, к счастью, достаточно поставить ограничение на метод. Так

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

(на play)

Это, наконец, делает его прозрачным для реализаторов других версий.

См. Также this users thread.

2

Вам нужна дополнительная as_trait функция Trait и его реализация:

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

Вы можете попробовать его на детской площадке. (trait objects)

+0

«объекты с несимметричным типом» имеют размер; два 'usizes'. Голый признак сам по себе, без объекта, является нестандартным типом. –

+0

Можете ли вы объяснить, почему нужен метод 'as_trait()', а 'use_trait (self as & Trait)' не работает непосредственно в 'provided()'? –

+0

@ChrisEmerson, я добавлю объяснение самому вопросу. Это довольно очевидно, почему это не работает простым способом. –