Use in const blocks / how to manage imports in procedural macro's generated code


#1

I’m trying to write a procedural macro that implements a #[derive()] trait for a struct. In the generated implementation code I need to use external crates so to avoid multi crate imports, and based on what I read in some other crate’s code (e.g., serde) I put my generated code into a const _IMPL_xxx_FOR_xxx : () = { /* generated code */ }; block but it fails to compile.

I was able to replicate my issue with the following code

const BLOCK_1: () = {
    use std::any::Any;
    use std::collections::HashMap;
    use core::option::Option;
};

fn main() { }

Here is what I get from the compiler

error[E0432]: unresolved import `core::option::Option`
 --> src/main.rs:4:9
  |
4 |     use core::option::Option;
  |         ^^^^^^^^^^^^^^^^^^^^ Maybe a missing `extern crate core;`?

error: aborting due to previous error(s)

Based on some discussion on the topic, I was told that the reason is that use statements typically assume a path relative to the root, and there is no way to explicitly name the block you are in (self refers to the current module unfortunately).

  • Is there any way to solve this? Why does it not work with some crate and does with some other?
  • What is the recommended way to manage external crate’s import in code generate by procedural macros?

Thanks :dizzy_face:


#2

What if you use std::option::Option?

Updated: meh, nevermind. You were trying to demonstrate the issue using core. My mistake.


#3

Then it compiles.

Indeed, the actual issue I have is when I try to import anymap, as per example below. I tried to replicate the issue using other crates and by sharing that issue more widely we discovered that references to the core crate didn’t work too. It might illustrate that the issue is not specific to “anymap”

const BLOCK_1: () = {
    extern crate anymap;
    use anymap::AnyMap;
};

#4

Can you provide examples of crates that work? If std is the only crate, I think that’s because Rust automatically imports it as a preamble (a set of useful imports automatically provided for the programmer to reduce boilerplate).

I’m not keen on how procedural macros work. If your procedural macro is provided as a crate, and your generated code knows which crates it needs, could your crate import those dependencies itself (e.g. extern crate anymap), then expose them as part of your crate’s API (e.g. pub use anymap)?

Your generated code would then use the crate’s API to reference the imports or something like that.

const BLOCK_1: () = {
    use std::any::Any;
    use std::collections::HashMap;
    use $crate::anymap::AnyMap;
};

fn main() { }

Disclaimer: I have no idea if this is even valid Rust.


#5

It works :thumbsup: thanks !!

FYI here is the synthax I used

pub extern crate anymap;

#6

Amazing. I wasn’t certain that idea would even work. Cheers!