11

I'm trying to implement PartialEq in Rust for a trait that has subtypes, so that I can add them as boxed pointers to a container and later compare them.

Here's my scaled-down implementation:

use std::any::Any;

trait Foo: Any {}

struct Bar {}

impl Foo for Bar {}

struct Baz {}

impl Foo for Baz {}

impl PartialEq for Foo {
    fn eq(&self, other: &Foo) -> bool {
        let me = self as &Any;
        let you = other as &Any;
        if me.is::<Bar>() && you.is::<Bar>() {
            true
        } else if me.is::<Baz>() && you.is::<Baz>() {
            true
        } else {
            false
        }
    }
}

fn main() {
    let bar: Bar = Bar {};
    let baz: Baz = Baz {};
    let foo1: &Foo = &bar;
    let foo2: &Foo = &baz;
    println!("{:?}", foo1 == foo2);
}

Code example in Rust Playground.

When I build this, I get:

rustc 1.17.0-nightly (0648517fa 2017-02-03)
error: non-scalar cast: `&Foo + 'static` as `&std::any::Any + 'static`
  --> <anon>:15:18
   |
15 |         let me = self as &Any;
   |                  ^^^^^^^^^^^^

error: non-scalar cast: `&Foo + 'static` as `&std::any::Any + 'static`
  --> <anon>:16:19
   |
16 |         let you = other as &Any;
   |                   ^^^^^^^^^^^^^

error: aborting due to 2 previous errors

which is confusing. Any ideas what I'm doing wrong here?

Edit: I don't believe this is a duplicate of Why doesn't Rust support trait object upcasting?, because what I'm trying to do is downcast using Any, not upcast.

Further Edit: Yes, this is a duplicate - sorry, I was thinking about what I was trying to do (downcast to the Bar and Baz types) rather than how I was doing that (upcasting to Any). However, that being said, I guess I still don't understand why the Any example, where they do this: let value_any = value as &Any; works, where mine doesn't. That being said, Joshua Entrekin did give a great answer.

Final Edit An, never mind, it's because I'm upcasting a trait rather than a type - Doh!. Thanks, everyone!

Community
  • 1
  • 1
Jack Lund
  • 131
  • 1
  • 6
  • I'm too new to the language to tell you all of your options, but it looks a bit like you're trying to do OO style sub-typing. Have you tried using an enum of some kind? – Justin Blank Feb 05 '17 at 19:49
  • 1
    @JustinBlank Tried that, but there's nowhere to put the num AFAICT - you can't add an enum to a trait, and if I make a global enum, it feels very kludgey. The docs for Any imply that if your type is an Any, you should be able to do the kind of casting I'm doing - hence my question. – Jack Lund Feb 05 '17 at 20:30
  • Your edit doesn't make sense to me, and I'm thinking that there's a terminology difference. You are trying to convert a `&Foo` to an `&Any`, *going upward in the hierarchy*. That's upcasting. Why do you say you are trying to downcast? [@JoshuaEntrekin also provides an example](https://play.rust-lang.org/?gist=3aae5b8349e3420497f3aa24f9e8ea7b&version=nightly&backtrace=0) that seems to work. – Shepmaster Feb 05 '17 at 20:44
  • @Shepmaster Sorry, I commented on his response, but didn't update my post. See above. – Jack Lund Feb 05 '17 at 22:02

1 Answers1

13

This is a duplicate of Why doesn't Rust support trait object upcasting? because you are trying to upcast from Foo to Any. If you add an as_any method to Foo and implement on it, this code can be made to work:

use std::any::Any;

trait Foo: Any {
    fn as_any(&self) -> &Any;
}

impl<T: Any> Foo for T {
    fn as_any(&self) -> &Any {
        self
    }
}

struct Bar {}

struct Baz {}

impl PartialEq for Foo {
    fn eq(&self, other: &Foo) -> bool {
        let me = self.as_any();
        let you = other.as_any();
        if me.is::<Bar>() && you.is::<Bar>() {
            true
        } else if me.is::<Baz>() && you.is::<Baz>() {
            true
        } else {
            false
        }
    }
}

fn main() {
    let bar: Bar = Bar {};
    let baz: Baz = Baz {};
    let foo1: &Foo = &bar;
    let foo2: &Foo = &baz;
    println!("{:?}", foo1 == foo2);
}

I show it here in the Playground.

Community
  • 1
  • 1
  • Thank you very much for doing the right thing and pointing out the duplicate. I hope you hang around [tag:rust] and help identify more dupes, and maybe have the chance to answer some non-duplicates too! ^_^ – Shepmaster Feb 05 '17 at 20:50
  • Trait objects without an explicit `dyn` is a hard error in Rust 2021. Therefore here the updated [Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e399193db4c5b5ef22d4ff38e73a2bc9). – Kaplan Jul 16 '21 at 10:56