Detecting a trait impl in proc macro

I have a proc macro that implements some trait for a struct. One of the functions of that trait should behave differently depending on if struct implements certain trait or not.

For simplicity, let's assume that trait I'm checking is ToString and I need to derive implementation for the function fn render(&self) -> String, which will work in the following way:

  1. If target struct implements ToString, it will return "[struct.to_string()]: [some data from fields]".
  2. If target struct does not implement ToString, it will return "(unnamed): [some data from fields]".

Proc macro cannot make queries to the type system, so it doesn't know if target struct implements ToString or not (excluding some trivial cases where we can assume it implements trait by scanning through #[derive(..)] attributes).

One solution I found is to unconditionally implement a helper trait that will have two (overlapping) implementations where one specializes another, depending on ToString trait:

// This vvv is required!
#![feature(specialization)]

trait Named {
    fn name(&self) -> String;
}

impl <T> Named for T {
    default fn name(&self) -> String {
        "(unnamed)".to_string()
    }
}

impl <T: ToString> Named for T {
    fn name(&self) -> String {
        self.to_string()
    }
}

// Now we can call Named::name(..) for every type, no matter if it implements `ToString` or not!

Another option is to simply define two derive macros, one for MyTraitWithoutToString and another for MyTraitWithToString, however, that reduces ergonomics and is error prone because now end-user have to think which one applies in a given situation.

Is there any way to do that without using trait specialization (which seems to be unsound, requires nightly and not going to stabilize anytime soon, if I understand correctly), but at the same time keeping the same ergonomics?

1 Like

No there is not really another way to do it, an example from std i that does this is Clone, it will change behaviour if something is Copy or not, and it only checks other derives to see if Copy was derived. One thing you could do is add a custom attribute that will signal that the have a to_string impl.