I have the following trait:
trait MyTrait {
type A;
type B;
fn foo(a: Self::A) -> Self::B;
fn bar(&self);
}
There are other functions like bar
that must be always implemented by the user of the trait.
I would like to give foo
a default implementation, but only when the type A = B
.
Pseudo-Rust code:
impl??? MyTrait where Self::A = Self::B ??? {
fn foo(a: Self::A) -> Self::B {
a
}
}
This would be possible:
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");
}
}
Is that possible in Rust?
Extending user31601's answer and using the remark from Sven Marnach's comment, here's an implementation of the trait with additional functions using the "delegate methods" pattern:
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
You can provide a default implementation in the trait definition itself by introducing a redundant type parameter:
trait MyTrait {
type A;
type B;
fn foo<T>(a: Self::A) -> Self::B
where
Self: MyTrait<A = T, B = T>,
{
a
}
}
This default implementation can be overridden for individual types. However, the specialized versions will inherit the trait bound from the definition of foo()
on the trait so you can only actually call the method if 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 also has an unstable impl specialization feature, but I don't think it can be used to achieve what you want.
Will this suffice?:
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
As noted, it'll bump into problems if MyTrait
as other methods that MyTraitId
can't provide an implementation for.