Broken diagnostic `trivial_casts` from Rust?

Apparently trivial_casts is not on by default, but it was turned on in the project I'm working on. However I'm wondering if it is broken:

use std::any::{Any, TypeId};
use std::hash::{DefaultHasher, Hash, Hasher};

trait DynKey: Any {
    fn eq(&self, other: &dyn DynKey) -> bool;
    fn hash(&self) -> u64;
    fn type_name(&self) -> &'static str;
}

impl<T: Eq + Hash + 'static> DynKey for T {
    #[warn(trivial_casts)]
    fn eq(&self, other: &dyn DynKey) -> bool {
        if let Some(other) = (other as &dyn Any).downcast_ref::<T>() {
            return self == other;
        }
        false
    }

    fn hash(&self) -> u64 {
        let mut h = DefaultHasher::new();
        // mix the typeid of T into the hash to make distinct types
        // provide distinct hashes
        Hash::hash(&(TypeId::of::<T>(), self), &mut h);
        h.finish()
    }
    
    fn type_name(&self) -> &'static str {
        std::any::type_name::<T>()
    }
}

(playground: Rust Playground)

gives the warning:

warning: trivial cast: `&(dyn DynKey + 'static)` as `&(dyn Any + 'static)`
  --> src/lib.rs:13:30
   |
13 |         if let Some(other) = (other as &dyn Any).downcast_ref::<T>() {
   |                              ^^^^^^^^^^^^^^^^^^^
   |
   = help: cast can be replaced by coercion; this might require a temporary variable
note: the lint level is defined here

Removing the cast doesn't work, as downcast_ref is then not found.

        let other_any = other as &dyn Any;
        if let Some(other) = other_any.downcast_ref::<T>() {
            return self == other;
        }

Just moves the warning to another line. And I don't really see how

let other_any: &dyn Any = other;

(which does work) is any cleaner to begin with. So it seems there is no reasonable way to solve this apart from allow? In general I do like this lint, it just seems to be rather incorrect in this case.

Is it worth filing an issue over this?

So what happens when you write other as &dyn Any is that it first performs a trait downcasting coercion, and then it performs an as cast from &dyn Any to &dyn Any. The as cast doesn't actually perform the coercion - it just helps provide the type hints needed to make the coercion happen.

I think it probably does make sense to file an issue in this case.

1 Like

The lint description does say:

Yes I saw that (in the mdbook), but if we want it to be possible for this to become a default lint, we need to get rid of the false negatives first. As such I'm leaning towards filing an issue.

How do you feel about using <dyn Any>::downcast_ref::<T>(other) instead of the let binding?

Love the turbofish in general. But I would not have come up with that one. It is unintuitive enough that I would want the compiler to suggest it.

EDIT: And I don't know that it is really readable.

Filed the case as `trivial_casts` diagnostic creates spurious errors when working with `Any` · Issue #148219 · rust-lang/rust · GitHub