Preventing a macro from "polluting" the call site with imported types


#1

Hello,

I’m writing an app that lets you make osx Dock applets from Rust. I have defined a macro in a library that abstracts a bunch of objective-c boilerplate setup. The macro works, but when I use it, all the various objective-c types it uses get “pulled in” to the call site, which means that site needs to import many of the same modules or else it fails to compile.

So I have this as the call site:

Which requires a bunch of objective c imports in that file, so that the macro, defined here, compiles.
(Note I couldn’t include a preview link because, as a new user, I have a 2 link quota)
macro definition

It makes sense to me why this would occur, since its just expanding the macro at the call site. Is there a way to prevent it? It seems like I might be able to use $crate in the macro definition in some way, but so far my efforts have failed. I have studied the Book and serde (which uses a lot of macros) but haven’t seen an example of how to resolve this.


#2

Use fully-qualified names within the macro. For example, rather than Result, use ::std::result::Result.

Also, keep in mind that you can use $crate::X to access name X in the crate where the macro was defined.


#3

Thanks for replying. Using the fully qualified path, or $crate, does seem to help with some types.

It doesn’t seem to help with traits though, for instance
objc_foundation::object::INSObject
is a Trait, but in the sample app I get

src/main.rs:49:5: 62:23 note: in this expansion of add_fly_item! (defined in <barfly macros>)
<barfly macros>:7:42: 7:54 help: items from traits can only be used if the trait is in scope; the following trait is implemented but not in scope, perhaps add a `use` for it:
<barfly macros>:7:42: 7:54 help: candidate #1: use `objc_foundation::object::INSObject`

even with either one of these in the library macro:
::objc_foundation::object::INSObject
or
$crate::INSObject

The lib compiles successfully even though the sample app fails.

Also, is there a fully qualified syntax for macros? The library is using some objc macros that I also don’t want to creep into the sample app. I tried ::msg_send! in the library, and that works there, but when I remove the associated crate from the sample app, it fails to build with error: macro undefined: 'msg_send!'

BTW I found your “Little book of rust macros” and I am reading it now, perhaps I will be enlightened.


#4

Either use uniform function call syntax (UFCS), or use the trait in the expansion:

path::to::Trait::method(&explicit_self)

<path::to:Type as path::to::Trait>::method(&explicit_self)

use path::to::Trait;
explicit_self.method()

As for macros, there is no qualification syntax for those. They have one name, and that name cannot be qualified. You can keep a macro private by not flagging it with #[macro_export].


#5

Thanks. With your advice I was able to eliminate most of the dependencies. The key steps are:

  1. use $crate (and therefore pub use for external types) in the library to prevent type leakage. Using :: syntax in the macro appears to work at first, but then you can’t remove the extern crate references from the call site.
  2. use UFCS (especially the angle-bracket variant) type deal with Traits in the macro.

Its too bad that macros can’t be qualified. I have a lingering dependency on objc in the main program that I can’t eliminate because my macros use sel! and msg_send! from that crate. It looks like I’m going to need to replicate (i.e. copy-paste) those macros into my code to make it work. Perhaps the upcoming revision to to the macro system will improve this…

BTW, your book on Rust macros is very informative.