How can I use my derive macro from the crate that declares the trait?

Hey, everyone!

We have two crates in our project:

  • Crate foo declares a trait MyTrait.
  • Crate foo_derive implements a derive macro for MyTrait. In order to refer to the types/traits in foo, foo_derive uses it with ::foo::*. All code generated by the macro (including uses) is wrapped in a const _: () = { ... }; to avoid polluting the caller's scope.

This has worked fine for using the macro from a third crate, but now I want to use it from crate foo lest I have to implement the trait manually. Is this possible? Simply using it ends up throwing the following error:

error[E0463]: can't find crate for `foo`
  --> crates/foo/src/my_trait.rs:10:10
   |
10 | #[derive(MyTrait)]
   |          ^^^^^^^ can't find crate
   |
   = note: this error originates in the derive macro `MyTrait` (in Nightly builds, run with -Z macro-backtrace for more info)

I assume this means I can only refer to foo as crate when I'm inside it, but the macro can't tell the difference. Is there a standard/recommended way of performing these imports in generated code?

Any help is much appreciated. Thanks in advance!

You can write extern crate self as foo; in the lib.rs of your foo crate precisely for this reason :slightly_smiling_face:


Aside: try not to use use … "statements" in your proc-macro code, since you may still be clashing with what your caller provided:

  1. A generic parameter may be called like your trait:

    struct Example<MyTrait> {
        field: MyTrait,
    }
    

    Good luck writing impl<MyTrait> MyTrait for Example<MyTrait> :grinning_face_with_smiling_eyes:

  2. Some proc-macros need to mention the type of the field in the emitted code (e.g., <#FieldType as MyTrait>::…).

    In that case, you may clash with #FieldType being some type called MyTrait, or the caller of your derive having, themselves, defined some trait MyTrait, and then having #FieldType = Box<dyn MyTrait>.

    If you have a use ::foo::*; then if the caller had a MyTrait of their own in scope, you won't be implementing your trait but theirs; if you have a use ::foo::MyTrait;, then you won't be correctly mentioning the actual #FieldType but rather the type Box<dyn ::foo::MyTrait> instead.

TL,DR: avoid use … "statements" as much as possible among your emitted code, and instead, write maximally full paths whenever possible :wink:

2 Likes

Thank you so much for the reply and for the bonus suggestion on use!

1 Like