How to test whether a variable implement specific trait

I want to test whether some variable implement specific trait, and found code as following

macro_rules! implements {(
    $T:ty : $($bounds:tt)*
) => ({
    use ::core::marker::PhantomData;

    trait DefaultValue {
        fn value (self: &'_ Self) -> bool { false }
    }
    impl<T : ?Sized> DefaultValue for &'_ PhantomData<T> {}
    trait SpecializedValue {
        fn value (self: &'_ Self) -> bool { true }
    }
    impl<T : ?Sized> SpecializedValue for PhantomData<T>
    where
        T : $($bounds)*
    {}
    (&PhantomData::<$T>).value()
})}

This macro can test whether a type implements a specific trait, but I need test a variable not a type. So I try to modify code as following

macro_rules! val_implements {(
    $V:ident : $($bounds:tt)*
) => ({
    use ::core::marker::PhantomData;

    trait DefaultValue<T: ?Sized> {
        fn value(self: &'_ Self, _: &T)-> bool { false }
    }
    impl<T: ?Sized> DefaultValue<T> for &'_ PhantomData<T> {
    }
    trait SpecializedValue<T: ?Sized> {
        fn value(self: &'_ Self, _: &T)-> bool { true }
    }
    impl<T: ?Sized> SpecializedValue<T> for PhantomData<T>
    where 
        T: $($bounds)* 
    {
    }
    (&PhantomData).value(&$V)
})}

But it does not work. How to test whether a variable implements a specific trait? Pls help, thanks.

You can try to add some

fn phantom_data_of_value<T: ?Sized>(_: &T) -> PhantomData<T> {
    PhantomData
}

to the original macro and use

(&phantom_data_of_value(&$V)).value()

I haven't tested that, but it might work.

Thanks for reply.
But it does not work. When test a variable which has not implemented the specific trait, the compiler will complain the variable not implements that trait

Please give more details. It seems to work for me.

In general it’s more helpful if you give full code examples with full error messages whenever possible. (My previous answer was shorter because I was on my phone. Actually… had you provided a playground in the original post, I could’ve probably modified that one even on mobile :wink:.)

1 Like

Thanks for your reply and suggestion.
Maybe there is another implict problem. I test here, it indeed does not work.
When I change code from val_implements!(b: Fn()->()) to val_implements!(b: FnMut()->()), it will be compiled successfully.

Alright, I’ll book that under “closure type inference is weird”. BTW, another thing to note is that this macro will give “somewhat inaccurate” false results in generic functions. E.g.

fn foo<T>(t: T) {
    val_implements!(f: SomeTrait); // <- will be false
    // even though `foo::<SomeType>` could be called with
    // `SomeType: SomeTrait`
}

to be honest, I’m not entirely sure what is a good use-case for this macro. Maybe you have one, I’d be curious what you need it for.

1 Like

Thanks for reply. I am studying Rust and want a method to get trait list that a variable implement in some case.
It looks this macro does not cover all the case. I wonder whether is there a method which can check a variable whether implements a specific trait.

We can do better with specialization, which is nice (even though I would never do it):

#![feature(specialization)]
#![allow(incomplete_features)]

macro_rules! implements {
    ($T:ty: $($bounds:tt)+) => {{
        trait Impls {
            const IMPLS: bool;
        }
        impl<T: ?Sized> Impls for T {
            default const IMPLS: bool = false;
        }
        impl<T: ?Sized + $($bounds)*> Impls for T {
            const IMPLS: bool = true;
        }
        <$T as Impls>::IMPLS
    }};
}
1 Like

I suppose this should best be solved by some external tooling / IDE in the future. With the ongoing “librification” of compiler components, e.g. in particular chalk, I’m positive that we’ll eventually be able to

  • hover over a variable to see its type (already possible); and them
  • click some button on/around that type to get a list of traits that it implements

in your IDE.

The best form of “list of all trait implementation” you’ll get right now is in the documentation. This doesn’t give you information for concrete types (e.g. Box<Option<Result<i32, io::Error>>> or whatever) but you could look at the documentation for Box, Option, etc. individually and piece the relevant information together yourself.

The documentation available online doesn’t do well with documenting cross-trait implementations either, in particular if the crate defining the trait depends on the crate defining the implementing type. This can be solved partially by generating docs locally for your projects (cargo doc or cargo doc --open) but even this way you won’t get a list of trait implementations for types from the standard library.

1 Like

Thanks for your patience reply.
I very expect some external tooling/IDE that your predict :slight_smile:
Specifically, I want to test which trait(Fn/FnMut/FnOnce) that the closure implemented in different case.

Thanks for reply.
This macro can check a type , but I want to check whether a variable implements specific trait.

If this is just about gathering personal knowledge in situations where you aren’t sure which Fn-* traits are implemented, you can just work with compiler errors to find out what you want.

E.g.:

fn main() {
    let mut a = vec![1; 10];
    let b = || {
        a[0] = 2;   
        println!("in closure: {:?}", a)
    };
    
    &b as &dyn FnMut() -> (); // no error
    &b as &dyn Fn() -> (); // error, so the closure doesn’t implement it

    // btw, `FnMut() -> ()` can also be written `FnMut()`
}

Rust Playground

Yes. But that time I want if there is a common method that can check whether a variable implements specific trait, it will be perfact.

What are you referring to by “method”?

A method – as in – a method that you can call with method syntax, i.e. variable.implements_certain_trait()? I disagree that such a method makes sense. Whether some type implements a trait is very much a compile-time concept. Usually you’ll already know by looking at your code and having some general knowledge of the types and trait involved, etc, if your variable’s type implements a trait you’re interested in. If you don’t know it, then you’ll want to ask the compiler to get an answer at compile time. Producing a bool value is something you don’t need, compiler error messages are enough. A bool is useful for influencing run-time behavior based on some condition, like in an if statement.

If you’re meaning “a common method that can check” like “an easy / general way to get this information” – so “method” as in “methodology” or “a way to do something”, then – well – then in the future improvements to IDEs would be useful, as I mentioned, and also – in the meantime – writing something that does or doesn’t generate compilation errors depending on the answer to the question “does this variable’s type implement that trait” already constitutes an adequate method in my opinion.

I sometimes write short helper functions for testing these assertions myself, in general a macro like

macro_rules! assert_val_implements {
    ($V:ident : impl $($bounds:tt)*) => {
        {
            fn assert_implements<T: $($bounds)*>(_: &T) {}
            assert_implements(&$V)
        }
    }
}

fn main() {
    let a = true;
    let b = Box::new(true);
    assert_val_implements!(a: impl Copy); // succeeds
    assert_val_implements!(b: impl Copy); // fails
}

can help.

Rust Playground

1 Like

Thank you very much.
"the method" what I means is "a way" to solved this problem, not method in Rust.
I intergrate many case I have studyed together, a compilation error will break the whole program to preceed. So I originally think a bool value is available choice.
Maybe there is no common way to check this in Rust today, I think the way you give is good enough for me :slight_smile:
Thank you again for patience help.

There is a case which need to clarified.

fn confuse_closure()-> impl FnOnce()->() {
    ||  {
        println!("hello");
    }
}

macro_rules! assert_val_implements {
    ($V:ident : $($bounds:tt)*) => {
        {
            fn assert_implements<T: $($bounds)*>(_: &T) {}
            assert_implements(&$V)
        }
    }
}

fn main() {
    let a = confuse_closure();
    //compilation error: expected an `Fn<()>` closure, found `impl FnOnce<()>`
    assert_val_implements!(a: Fn()->());
}

In this case, the closure in fn confuse_closure should implements Fn()->(), but with explicit declaration of fn confuse_closure, its return value looks not implements Fn()->(). So does a implement Fn()->() in the end?

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.