fadedbee
fadedbee

Reputation: 44787

Variable does not need to be mutable, but it does

I have a macro which works:

#[macro_export]
macro_rules! btreemap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::BTreeMap::new();
         $( map.insert($key, $val); )*
         map
    }}
}

but which the compiler warns about:

warning: variable does not need to be mutable
  --> src/util.rs:16:11
   |
16 |          let mut map = ::std::collections::BTreeMap::new();
   |              ----^^^
   |              |
   |              help: remove this `mut`
   | 
  ::: src/foo.rs:49:13
   |
79 |                 foo: btreemap!{},
   |                      ----------- in this macro invocation
   |
   = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

If I remove the mut, I get an error:

error[E0596]: cannot borrow `map` as mutable, as it is not declared as mutable
   --> src/util.rs:17:10
    |
16  |          let map = ::std::collections::BTreeMap::new();
    |              --- help: consider changing this to be mutable: `mut map`
17  |          $( map.insert($key, $val); )*
    |             ^^^ cannot borrow as mutable
    | 
   ::: src/bar.rs:110:18
    |
116 |         let bar = btreemap!{quux => wibble};
    |                   -------------------------- in this macro invocation
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

i.e. If mut it warns on empty invocations, but if it's not if errors on non-empty invocations.

How can I fix this?

Upvotes: 7

Views: 2520

Answers (2)

user4815162342
user4815162342

Reputation: 155126

How can I fix this?

You can suppress the warning by adding #[allow(unused_mut)] to the code generated by the macro:

macro_rules! btreemap {
    ($( $key: expr => $val: expr ),*) => {{
        #[allow(unused_mut)]
        let mut map = ::std::collections::BTreeMap::new();
        $( map.insert($key, $val); )*
        map
    }}
}

The warning is about as benign as a warning can get, and warning suppressions of this kind are often seen in macro definitions.

Another possibility is the one you already discovered, to special-case empty expansion. But I'd prefer the explicit warning suppression because it eliminates the (bogus) warning without complicating the macro. The special case you introduced wasn't actually necessary - the exact same code would be generated without it.

Upvotes: 8

fadedbee
fadedbee

Reputation: 44787

Fixed by adding a special case for the empty invocation:

#[macro_export]
macro_rules! btreemap {
    () => {{
        ::std::collections::BTreeMap::new()
    }};
    ($( $key: expr => $val: expr ),*) => {{
        let mut map = ::std::collections::BTreeMap::new();
        $( map.insert($key, $val); )*
        map
    }};
}

Upvotes: 7

Related Questions