Default trait method implementation for all trait objects
Asked Answered
L

1

2

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

  • I don't think I can use self in that case, so it doesn't work.
  • If I use trait MyTrait: PartialEq then I can't create trait objects anymore.
Lumpfish answered 24/3, 2018 at 15:2 Comment(2)
Thinking out of the box, rename MyTrait to MyTraitBase and MyTraitComparable to MyTrait — voilà: you are using MyTrait everywhere.Erving
@Erving perhaps an improvement, thanks, but not quite everywhere since all the structs must implement MyTraitBase.Lumpfish
C
2

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)
}
Cosetta answered 24/3, 2018 at 21:48 Comment(1)
Although I don't use nightly, it's good to know there is a solution under consideration!Lumpfish

© 2022 - 2024 — McMap. All rights reserved.