2

A tricky problem. I have to implement a function fn eq( a, b ) comparing a and b. The function should return false if either types of variables are different or variables have different values. The function should return true if both type and value are the same.

A possible solution is to use dyn Any as Netwave advised. But such a solution has limited application because it restricts arguments of eq with static constraint. Maybe it is possible to come up with a more practical implementation? Playground of such a solution.

Kos
  • 1,547
  • 14
  • 23
  • Is this homework? What is a realistic scenario where you have two variables of different types, but need a comparison function to tell you so? – Herohtar Oct 09 '21 at 05:02
  • Related/duplicate: https://stackoverflow.com/questions/41596628/how-to-match-on-data-type-in-rust – Herohtar Oct 09 '21 at 05:03
  • Hello Herohtar. No it's not homework. Purpose is self-education. Realistic application in the system of evaluation logical expressions. No it's not duplicate, its very good question. Question you refer does not answer my question. Why stackoverflow is so often such negative? – Kos Oct 09 '21 at 05:31
  • I was just wondering if it was homework because it didn't sound like a problem one might encounter in a typical scenario, and homework questions are required to be identified as such. Did you read the answer to the other question? At the very bottom it shows a way to compare the types of two variables. Once you do that, comparing the values should be obvious. – Herohtar Oct 09 '21 at 05:49
  • 2
  • 2
    @kos, please do not edit answered questions to update with more requirements. Keep them simple and in case something else bubble up you can open another related question. I thin you are overcomplicating things: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c291d9a19db991d8c5c554312a6a7473 – Netwave Oct 09 '21 at 06:46
  • @kmdreko it should give true only if both type is the same and value is the same. imagine comparing 2 logical expressions. – Kos Oct 09 '21 at 06:57
  • @Netwave but opening a new question for a bit more restricted version also not an option, right? how to be? – Kos Oct 09 '21 at 06:58

1 Answers1

3

Well, playing with Any is not so difficult to implement something:

use std::any::Any;
use std::any::TypeId;

fn eq<T: Any + Eq, Q: Any + Eq>(a: T, b: Q) -> bool {
    if TypeId::of::<T>() == TypeId::of::<Q>() {
        let b_as_t = &b as &dyn Any;
        // safe to unwrap, we matched the type already
        &a == b_as_t.downcast_ref::<T>().unwrap()
    } else {
        false    
    }
}

fn main() {
    assert!(!eq("foo", 1));
    assert!(eq(1, 1));
    assert!(eq(&1, &1));
    assert!(!eq(&'a', &1));

}

Playground

As per the comments, it may be possible to have another version that works over references directly:

use std::any::Any;
use std::any::TypeId;

fn eq<T: Any + Eq, Q: Any + Eq>(a: &T, b: &Q) -> bool {
    if TypeId::of::<T>() == TypeId::of::<Q>() {
        let b_as_t = b as &dyn Any;
        // safe to unwrap, we matched the type already
        *a == *b_as_t.downcast_ref::<T>().unwrap()
    } else {
        false    
    }
}

fn main() {
    assert!(!eq(&"foo", &1));
    assert!(eq(&1, &1));
    assert!(eq(&1, &1));
    assert!(!eq(&'a', &1));
    let s1 = "foo".to_owned();
    let s2 = "foo".to_owned();
    assert!(eq(&s1, &s2));
}

Playground

Netwave
  • 40,134
  • 6
  • 50
  • 93
  • Thank you Netwave, that works, but it has limited application because it restricts arguments of `eq` with `static` constraint. Maybe it is possible to come up with a more practical implementation? I have the problem demonstrated in the playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7a57cdf10086dcb09adf8a0f79c28e20 – Kos Oct 09 '21 at 06:30
  • @Kos, maybe you are overcomplicating things? https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c291d9a19db991d8c5c554312a6a7473 – Netwave Oct 09 '21 at 06:44
  • I would say I am investigating what Rust can and cant. Such information shared might be useful for other adopters of Rust. – Kos Oct 09 '21 at 07:00
  • Unfortunately the `'static` requirement is more insidious than it appears at first. Let's say you want to compare two values that allocate, such as `Strings`. You'd like to compare them by reference, i.e. not to have `eq` consume the allocations just in order to compare them. But doing [the obvious](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1b0505780d373da4db9a1816c50daca5) doesn't compile because references aren't static. – user4815162342 Oct 09 '21 at 09:50
  • @user4815162342 indeed. Any suggestion? – Netwave Oct 09 '21 at 10:07
  • @Netwave A quick fix that makes it slightly better would be to accept references to begin with, [like this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=db9310986525e0b59789d9f544b2754f). It would still require the referred-to types to be `'static`, but at least you could compare values of such types (which include strings etc) without consuming them. – user4815162342 Oct 09 '21 at 10:43
  • @user4815162342 yeah, I actually updated the answer with that just with you previous message hint. Thanks olr the tips! – Netwave Oct 09 '21 at 16:03
  • Thanks, this makes the answer much more useful for non-`Copy` types. It's a real shame one can't use `TypeId::of` on non-static types. – user4815162342 Oct 09 '21 at 17:24