我可以有条件地提供特征函数的默认实现吗?

时间:2019-04-11 08:49:19

标签: rust

我具有以下特征:

trait MyTrait {
    type A;
    type B;

    fn foo(a: Self::A) -> Self::B;

    fn bar(&self);
}

特征bar等其他功能必须始终由特征用户来实现。

我想为foo提供一个默认实现,但仅当类型为A = B时。

伪锈代码:

impl??? MyTrait where Self::A = Self::B ??? {
    fn foo(a: Self::A) -> Self::B {
        a
    }
}

这可能:

struct S1 {}

impl MyTrait for S1 {
    type A = u32;
    type B = f32;

    // `A` is different from `B`, so I have to implement `foo`
    fn foo(a: u32) -> f32 {
        a as f32
    }

    fn bar(&self) {
        println!("S1::bar");
    }
}

struct S2 {}

impl MyTrait for S2 {
    type A = u32;
    type B = u32;

    // `A` is the same as `B`, so I don't have to implement `foo`,
    // it uses the default impl

    fn bar(&self) {
        println!("S2::bar");
    }
}

在Rust中有可能吗?

3 个答案:

答案 0 :(得分:3)

您可以通过引入冗余类型参数在trait定义本身中提供默认实现:

trait MyTrait {
    type A;
    type B;

    fn foo<T>(a: Self::A) -> Self::B
    where
        Self: MyTrait<A = T, B = T>,
    {
        a
    }
}

可以为单个类型覆盖此默认实现。但是,专用版本将继承特征上的foo()定义所绑定的特征,因此只有A == B时,您才可以调用该方法:

struct S1;

impl MyTrait for S1 {
    type A = u32;
    type B = f32;

    fn foo<T>(a: Self::A) -> Self::B {
        a as f32
    }
}

struct S2;

impl MyTrait for S2 {
    type A = u32;
    type B = u32;
}

fn main() {
    S1::foo(42);  // Fails with compiler error
    S2::foo(42);  // Works fine
}

Rust也有一个unstable impl specialization feature,但我认为它不能用于实现您想要的功能。

答案 1 :(得分:2)

足够了吗?:

trait MyTrait {
    type A;
    type B;

    fn foo(a: Self::A) -> Self::B;
}

trait MyTraitId {
    type AB;
}

impl<P> MyTrait for P
where
    P: MyTraitId
{
    type A = P::AB;
    type B = P::AB;

    fn foo(a: Self::A) -> Self::B {
        a
    }
}

struct S2;

impl MyTraitId for S2 {
    type AB = i32;
}

Rust Playground

如前所述,如果MyTrait和其他MyTraitId无法为其提供实现的方法一样,就会出现问题。

答案 2 :(得分:0)

扩展user31601's answer并使用Sven Marnach的评论,这是使用“委托方法”模式的trait的附加功能实现:

trait MyTrait {
    type A;
    type B;

    fn foo(a: Self::A) -> Self::B;
    fn bar();
}

trait MyTraitId {
    type AB;
    fn bar_delegate();
}

impl<P> MyTrait for P
where
    P: MyTraitId,
{
    type A = P::AB;
    type B = P::AB;

    fn foo(a: Self::A) -> Self::B {
        a
    }
    fn bar() {
        <Self as MyTraitId>::bar_delegate();
    }
}

struct S2;

impl MyTraitId for S2 {
    type AB = i32;
    fn bar_delegate() {
        println!("bar called");
    }
}

fn main() {
    <S2 as MyTrait>::bar(); // prints "bar called"
}

Playground