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:
- If target struct implements
ToString
, it will return "[struct.to_string()]: [some data from fields]". - 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?