How to unambiguously refer to a particular crate from a proc-macro

Hi folks,

Is there a way to unambiguously refer to a particular crate from a proc-macro (specifically, a derive macro) that does not require that that crate be present in the caller's Cargo.toml?

E.g. so there is no ambiguity in the following, and so that the following works whether or not foo has a_certain_specific_crate as a dependency:

// foo/src/lib.rs
#[derive(foo_derive::Foo)]
struct MyStruct {}

// foo_derive/src/lib.rs
#[proc_macro_derive(Foo)]
fn foo_derive(_: TokenStream) -> TokenStream {
  quote!(
    pub fn whatever() { ::a_certain_specific_crate::do_something() }
  )
}

Is this possible? If not, is the best practice to re-export foo_derive and from a third crate, and also export a_certain_specific_crate?

Thank you!
Robert

Best practice seems to be to provide a "shim" crate that users depend on rather than the proc macro crate itself. Then, whenever the proc macro needs to name a type, it goes through that crate's exposed (hidden?) API.

As a high-profile example, see serde/serde-derive. The now-idiomatic way to use serde's derives is through the reexports that serde provides, and all paths that the macro emits start with ::serde, even the std types, which are shimmed through ::serde::exports::std (or similar, the exact path doesn't matter).

The idea is that the fact that the -derive crate has to be a separate crate from your runtime code should be transparent to the user.

Unfortunately, this can still break in one major case: dependency renaming. Because you can rename a dependency in Cargo.toml, your proc macro cannot know at compile time what name the runtime code is going to be available behind. In practice, most crates with proc macros just break if you rename the dependency.

If you must be correct in the face of crate renaming, crates such as proc-macro-crate will (attempt to) look up a crate in the Cargo.toml and give you the correct name that it's exposed as.

In the future, hopefully higeine can magically save us. (E.g. somehow a higeinic $crate could be made to resolve to the runtime crate... somehow.)

1 Like

Thank you! I appreciate the thorough reply.

Oh and I meant to put this in originally but forgot: another potential reason to avoid proc-macro-crate is that it breaks the "purity" of the proc macro in that it no longer just takes in the macro input to produce the macro output. Instead, it accesses the file system to get another input (the crate manifest). This prevents you from using things like watt which rely on this pure input/output relationship.

2 Likes

Wow, watt is something I didn't know existed, but I can certainly make use of. Thanks!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.