Check if trait is implemented in proc macro

Is it possible to check in proc macro if trait is implemented by type? If no, how than format_args! is implemented? I mean, it can check if display trait is implemented. Compiler magic?

No magic at all - it is checked by the code emitted with macro, not by macro itself. The only magic is the fact that formatting traits are special to get customized error messages.

Consider the following simple code:

struct NotDisplay;

fn main() {
    let _ = format_args!("{}", NotDisplay);
}

Playground

It is expanded to the following (can be compiled only with nightly, due to unstable features usage):

struct NotDisplay;

fn main() {
    let _ = ::core::fmt::Arguments::new_v1(
        &[""],
        &match (&NotDisplay,) {
            (arg0,) => [::core::fmt::ArgumentV1::new(
                arg0,
                ::core::fmt::Display::fmt,
            )],
        },
    );
}

Now, the error points to the argument of ArgumentV1::new call:

error[E0277]: `NotDisplay` doesn't implement `std::fmt::Display`
 --> src/main.rs:9:17
  |
9 |                 ::core::fmt::Display::fmt,
  |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ `NotDisplay` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `NotDisplay`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
  = note: required by `std::fmt::Display::fmt`

This method (and the struct itself, in fact) is not documented due to not being part of official public API, but here's it in the source code:

impl<'a> ArgumentV1<'a> {
    #[doc(hidden)]
    #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
    pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> {
         // snip
    }
}

Now let's try to reproduce the error with custom trait. We need to make a function which accepts two arguments: a value of some type, which is supposed to implement a trait (but which won't, in our case), and a method from this trait, accepting &self. Here's how it looks:

struct NotTrait;

trait Trait {
    fn method(&self);
}

fn use_trait<T>(_val: &T, _method: fn(&T)) {}

fn main() {
    use_trait(&NotTrait, Trait::method);
}

Playground

As you can see, the error is conceptually the same:

error[E0277]: the trait bound `NotTrait: Trait` is not satisfied
  --> src/main.rs:10:26
   |
4  |     fn method(&self);
   |     ----------------- required by `Trait::method`
...
10 |     use_trait(&NotTrait, Trait::method);
   |                          ^^^^^^^^^^^^^ the trait `Trait` is not implemented for `NotTrait`
3 Likes

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.