Keeping macros backward-compatible with 2015/2018 support

Previous discussions on semver-compatible version changes based on requiring a newer compiler version:

If Cargo.toml allowed min version numbers, then I would be able to get away with a minor version change for the scenario below. But without that, it’s not so clear to me whether I should just roll a new major version based on the discussions linked above. Since the following issue affects pretty much all crates providing macros and invoking helper macros, I wanted to ask what people’s general strategy has been.

Specifics (simplified from downcast-rs):
Say, a crate foo provides a macro compatible with a pre-1.30-version of the compiler:

macro_rules! bar {
    () => {};
    ($a:ident) => { bar!{} };
}

However, this doesn’t allow invoking the macro by module name: foo::bar!(baz) since the recursive call would also need to be namespaced. Updating the macro to use $crate:: for the recursive invocation addresses this at the expense of requiring rust >=1.30:

macro_rules! bar {
    () => {};
    ($a:ident) => { $crate::bar!{} };
}

I can’t safely do a minor version change for users of the old compiler since I can’t specify a min rust version on Cargo. I’m extremely averse to breaking anyone, implying that I should just make it a major version change. Have others being doing the same?

You may want to look at this:
https://doc.rust-lang.org/nightly/edition-guide/rust-2018/macros/macro-changes.html#macros-using-local_inner_macros

Example:

#[macro_export(local_inner_macros)]
macro_rules! hello{
    ()=>{
        
    };
    (recursive)=>{
        hello!()
    };
}

This attribute has the annoying limitation that it can’t reference macros outside the current crate,
so you will sometimes need to create other macros (without the #[macro_export(local_inner_macros)] attribute) to use ones declared outside the crate.

Thanks for your reply. I do indeed know about it, but my principal concern is with backward compatibility. Neither of these features work with older rust versions; so the semver backward compatibility concern still holds, and I’m curious as to how macro authors have handled this issue.

Older versions of Rust just ignore the (local_inner_macros) part of #[macro_export(local_inner_macros)],
so you can make the macros work with both use and #[macro_use] by just doing what the guide says.

That is what I did in type_level_value for the type_fn macro,which calls itself recursively.I also have CI to check that it works with Rust in stable and other versions including 1.20 (but forgot to add a 2018 edition crate to test this in CI).

I just created a 2018 edition crate with this code:

use type_level_values::{type_fn,type_fn::TypeFn};
type_fn!{
    pub fn Foo[v](v){ (v,v,v,v,v) }
}
fn main() {
    let _:TypeFn<Foo,u32>=(0u32,0u32,0u32,0u32,0u32);
}

and it works fine.

The approach using local_inner_macros as shown in the edition guide should work in any Rust compiler 1.0.0 or newer. It was designed to solve the problem you are solving of old compiler version support.

Ah, apologies! I didn’t read that part of the book carefully. Indeed, this is exactly what I need!

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