`cfg_attr` to allow unused variables doesn't work in patterns

I noticed some strange behavior when trying to destructure a struct and selectively ignore unused variables based on feature flags.

#![deny(warnings)]

fn main() {
#[derive(Default)]
        struct A {
            a: u8,
            b: u8
        }

        let A {
            //#[cfg_attr(not(feature = "my_feature"), allow(unused_variables))]
            a,
            #[cfg_attr(not(feature = "my_feature"), allow(unused_variables))]
            b,
        } = Default::default();
        //#[cfg(feature = "my_feature")]
        dbg!(a);
        #[cfg(feature = "my_feature")]
        dbg!(b);
}

The above code currently compiles fine. However, if I uncomment the two lines above, rustc complains about a and b being unused.

This is rather surprising, since I would imagine that the cfg_attr attributes would've allowed both a and b in the destructured pattern to be unused. I'm not sure why when I only have one cfg_attr, things work, but not when I add another one.

(I've also seen a similar "unused variable" issue even with only one cfg_attr; however, I was unable to reproduce this with a toy example. Unfortunately, I can't post the code here, since it is from my workplace.)

Can somebody explain to me what's going on here, and why cfg_attr is not behaving the way I would have expected it to?

This is... weird, but doesn't have anything to do with #[cfg_attr]. Specifically, even just writing

let A {
    #[macro_use]
    a,
    #[allow(unused_variables)]
    b,
} = Default::default();
let _ = a;

and putting any attribute[1] on the a subpattern makes it so that the #[allow] on the b subpattern has no effect.

This seems to just be a bug in the compiler. You can report it so it can potentially get fixed in the future.

In the right now, though, the workaround would be a let _ = binding; after the destructure, or alternatively just don't bind the unused binding at all, e.g.

let A {
    #[cfg(feature = "my_feature")] a,
    #[cfg(feature = "my_feature")] b,
    .. // disregard any unbound fields
} = /* source */;

  1. #[macro_use] is just a random attribute which happens to be allowed but have no effect in this position (other than an unused_attrubutes lint). #[allow()] or #[cfg(all())] work just the same. ↩︎

Thanks for the reply!

So from what it seems, if you already have an attribute somewhere in a pattern, and try to add another one, the second one gets ignored somehow...

This seems to just be a bug in the compiler.

Oh wow... :flushed:

Anyway, I also tried the following example -- note here that the #[macro_use] attribute is on the let statement itself, not within the pattern. And voila, I still get a complaint about unused variables...

#![deny(unused_variables)]

fn main() {
    #[derive(Default)]
    struct A {
        a: u8,
        b: u8
    }

   #[macro_use]
    let A {
        a,
        #[allow(unused_variables)]
        b,
    } = Default::default();
    dbg!(a);
}

(You also get a similar result if you place the #[macro_use] attribute on top of the main function.)

This seems to imply that even having an attribute on top of an "encompassing item" (not sure what the correct technical term is here) also results in the pattern's attribute getting ignored.

Anyway, I've created the following Github issue: Attributes within patterns do not work if there is another attribute in the same pattern or above · Issue #118682 · rust-lang/rust · GitHub

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.