提供方法cast& self to trait object

时间:2017-02-08 18:50:13

标签: rust traits

我有一个特征,我想提供一种方法。该方法将根据一些帮助者来实现,这些帮助者没有业务在特征内,并且非常简单,动态多态性比使它们通用更有意义。所以我的代码是

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

更糟糕,因为它根本不允许&TraitTrait因为类型未归类,所以Trait类型实现特征{ {1}})。

当然我可以简单地制作

Trait

但实际代码背后有很多,所以我不想在那里进行单态化,特别是因为特性总是作为特征对象处理。

有没有办法告诉Rust所有fn use_trait<T: Trait>(x: &T) 必须调整大小的类型,这里是一个应该适用于所有类型的方法的定义?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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的其他方法。

更新:实际上,重点是仍然可以覆盖provided

现在可以通过使其成为通用来进一步改进上述内容。有std::makrer::Unsize,在撰写本文时不稳定。我们无法做到

trait Trait : Unsize<Trait>

因为Rust不允许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