What's the best practice to write a macro to work in both no_std and std packages?
More details: I have a macro that uses some types and functions from fmt, char and hash modules, limited to the overlaps of std and core. To be able to name these items, I have to have the full path inside the macro code. Now, wether core is available as the top-level lang library, or std, that depends on the caller of the macro, but not the macro's package itself.
What I have started to do right now is to expect all caller package to explicitly have extern crate core; in the code, if they are not no_std. But this solution doesn't feel right.
Unfortunately, no_std is not testable at compile-time using cfg(). Macros are a good reason to actually make no_std a config, but I couldn't find any previous discussion from this perspective.
So, what do you think? Are there any better ways to handle this? Should no_std be a testable config in compile time?
Thanks, @ExpHP. Right, with $crate it's possible to move the dependency in-package. I'm now doing so.
About the feature, I've seen it named just std in many places, and recommended over use_std. So, I'm going with std.
Just one question: why did you say "enabled by default"? I'm actually going with no-std-by-default where possible, and have std feature only if there's extra API available that way.
Since almost all libcore is just reexported in libstd, there shouldn't be any harm to use core::* variants all the time, even when library is used in a use-std package. Is that the right assumption?
I thought this was the case (which is why I said "std feature") but had trouble actually finding an example.
Depends on the target userbase I guess. For applications where no_std doesn't have a very strong presence, functions with std types might look pretty innocuous to most people and it could be surprising if a feature is necessary to access them. (personally, no_std isn't something I dedicate much thought to myself)
Erp, sorry, I spoke too soon. Re-reading the discussion I see that everything you're using is in the overlap of core and std, so in that case I guess limiting it to core is no issue.
You shouldn't need a std feature if you only intend to use things from core. I usually define an export module to re-export the types my macro depends on.
extern crate core;
// Not public API.
#[doc(hidden)]
pub mod export {
pub use core::marker::PhantomData;
}
#[macro_export]
macro_rules! behnam {
($name:ident) => {
pub struct $name<T>($crate::export::PhantomData<T>);
}
}
Macros 2.0 will fix this in an even better way than pub(macro). As I understand it, the macro body will have access to everything that is in scope in the location the macro is defined (not in the location the macro is invoked), plus anything from the scope in which the macro is invoked that is explicitly passed to the macro. So basically it works just like how we are all used to scopes working when calling a function from another crate.