Mark
Mark

Reputation: 19997

Default trait method implementation for all trait objects

I have a trait MyTrait, and I want all trait objects &dyn MyTrait to be comparable to each other and to nothing else. I have that now based on How to test for equality between trait objects?.

The problem is that I need to use MyTraitComparable everywhere, instead of MyTrait. Is there a way to get around this?

use std::any::Any;

trait MyTrait {}

trait MyTraitComparable: MyTrait {
    fn as_any(&self) -> &dyn Any;

    fn equals(&self, other: &dyn MyTraitComparable) -> bool;
}

impl<S: 'static + MyTrait + PartialEq> MyTraitComparable for S {
    fn as_any(&self) -> &dyn Any {
        self as &dyn Any
    }

    fn equals(&self, other: &dyn MyTraitComparable) -> bool {
        match other.as_any().downcast_ref::<S>() {
            None => false,
            Some(a) => self == a,
        }
    }
}

#[derive(PartialEq)]
struct MyObj {
    a: i32,
}
impl MyObj {
    fn new(a: i32) -> MyObj {
        MyObj { a }
    }
}

impl MyTrait for MyObj {}

fn main() {
    assert!(as_trait_obj_and_compare(&MyObj::new(1), &MyObj::new(1)));
}

fn as_trait_obj_and_compare(obj: &dyn MyTraitComparable, another_obj: &dyn MyTraitComparable) -> bool {
    obj.equals(another_obj)
}

I tried moving as_any and equals to MyTrait and providing a default implementation, but

Upvotes: 2

Views: 1129

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65907

If you're willing to use a nightly compiler and unstable features, you can use specialization to avoid having two traits:

#![feature(specialization)]

use std::any::Any;

trait MyTrait {
    fn as_any(&self) -> &dyn Any;
    fn equals(&self, other: &dyn MyTrait) -> bool;
}

default impl<S: 'static + PartialEq> MyTrait for S {
    default fn as_any(&self) -> &dyn Any {
        self as &dyn Any
    }

    default fn equals(&self, other: &dyn MyTrait) -> bool {
        match other.as_any().downcast_ref::<S>() {
            None => false,
            Some(a) => self == a,
        }
    }
}

#[derive(PartialEq)]
struct MyObj {
    a: i32,
}
impl MyObj {
    fn new(a: i32) -> MyObj {
        MyObj { a }
    }
}

impl MyTrait for MyObj {}

fn main() {
    assert!(as_trait_obj_and_compare(&MyObj::new(1), &MyObj::new(1)));
}

fn as_trait_obj_and_compare(obj: &dyn MyTrait, another_obj: &dyn MyTrait) -> bool {
    obj.equals(another_obj)
}

Upvotes: 2

Related Questions