Resolve cyclical dependencies with macros

I am sure there is a best practice for this, I just haven't seen it.

I have a crate common, which defines several common things (lol) including the trait Jsonable

I have a common_macros crate where I define a derive macro Jsonable which references common::Jsonable (the trait) in order to implement it.

I had everything working wonderfully including:

  • Deriving the Jsonable macro in my common tests
  • Deriving the Jsonable macro in other crates (like networking)

But, when I tried to derive Jsonable in common itself, I got a missing reference error. It couldn't find the common::Jsonable trait.

After some research, I realized I had never made common a dependency of common_macros. Once I did that, the rust-analyzer error went away. I could derive Jsonable.

Except that when I built common, I got a cyclic dependency error. Because, of course, there is a cyclic dependency. common depends on common_macros and common_macros depends on common.

So, how am I supposed to do this? How do I:

  • Define a trait in common for Jsonable
  • Define a macro to derive Jsonable
  • Use that macro in the common crate itself

There's a specific syntax to help with this: in the common crate, write:

extern crate self as common;

and the macro will work within common in the same way that it does in other crates.

You should not make common_macros depend on common; it won't help and can't work, because proc-macro crates are compiled and loaded as compiler plugins, so any dependencies they have aren't accessible to the crate that is using those macros. (That's the entire reason proc-macro crates have to be separate crates at all.)

4 Likes

Thanks! I didn't think common_macros should depend on common. That syntax was exactly what I needed. I appreciate it.

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.