The Rust solution to this, more general than just to procedural macros, are public dependencies through public reexports:
//! Your `lib.rs`
// 2018 edition
pub use ::itertools;
// old style
pub extern crate itertools;
and then within a macro_rules! macro, for instance, you expand to a path referring to your crate first, which must contain the itertools crate as the second element of the path / as if it were a top-level module:
macro_rules! my_izip {(
$($input:tt)*
) => (
$crate // self crate
::
itertools
::
izip! { $($input) * }
)}
This way users of your crate will not need to depend on itertools on their own.
This is all well and fine, but with procedural macros things get a bit more complicated. Indeed:
-
there is no
$cratemagic metavar for procedural macros.-
You'll have to hardcode the name of your crate instead of
$crate:::name_of_my_crate::itertools::izip! { ... }
-
-
a
proc-macro = truecrate currently cannot export any items other than functions tagged with#[proc_macro],#[proc_macro_derive], or#[proc_macro_attribute]:
The solution to the second problem is easy, but a bit more cumbersome: a "normal" Rust crate that wants to define, among other things, procedural macros, needs, in practice, to be split in two crates:
-
the
proc-macro/derive/internals/helpercrate, which defines the procedural macros; -
and the "frontend" crate, which reexports the
proc-macrocrate, while defining all the other stuff.- (The name of the crate to hardcode in the expanded paths is that of the frontend crate)
This means you need to:
Create your helper proc-macro = true crate
Run
cargo new --lib src/proc_macros --name your_crate_name-proc_macros
(cd src/proc_macros && mv src/lib.rs mod.rs && rmdir src)
Add to src/proc_macros/Cargo.toml
[lib]
proc-macro = true
path = "mod.rs"
[dependencies]
proc-macro2.version = "1.0.x"
quote.version = "1.0.y"
syn.version = "1.0.z"
syn.features = [] # you may need "full" features
Edit src/proc_macros/mod.rs to your liking
Set up your frontend crate
Add to your Cargo.toml
[dependencies]
[dependencies.your_crate_name-proc_macros]
path = "src/proc_macros"
version = "..." # Same as main version
[workspace]
members = ["src/proc_macros"]
Add to your src/lib.rs
pub use your_crate_name_proc_macros::{
/* your procedural macros here */
};
#[doc(hidden)] pub /* hide from doc since it is just a tool for your macros */
use ::itertools;
// or
#[doc(hidden)] pub /* hide from doc since it is just a tool for your macros */
extern crate itertools;
What your procedural macro should expand to to refer to itertools' izip!:
src/proc_macros/mod.rs
use ::proc_macro::TokenStream;
use ::proc_macro2::{
Span,
TokenStream as TokenStream2,
};
use ::quote::{
quote,
quote_spanned,
ToTokens,
};
use ::syn::{*,
parse::{Parse, Parser, ParseStream},
punctuated::Punctuated,
Result, /* explicitly shadow `Result` */
};
#[proc_macro...] pub
fn your_proc_macro (/* args: TokenStream,*/ input: TokenStream)
-> TokenStream
{
let input = parse_macro_input!(input as /* ... */);
// ...
let izip = quote! {
::your_crate_name::itertools::izip
};
TokenStream::from(quote! {
/* your expansion here */
#izip! { /* ... */ }
})
}
- You could even reexport
izipdirectly (pub use ::itertools::izip) and have the proc-macro expand to::your_crate_name::izip.