变量不需要是可变的,但它确实

Variable does not need to be mutable, but it does

我有一个有效的宏:

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

但是编译器警告:

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)

如果我删除 mut,我会得到一个错误:

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)

即如果 mut 它会在空调用时发出警告,但如果不是,则在非空调用时出错。

我该如何解决这个问题?

已通过为空调用添加特殊情况进行修复:

#[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
    }};
}

How can I fix this?

您可以通过在宏生成的代码中添加 #[allow(unused_mut)] 来抑制警告:

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

警告是尽可能温和的警告,这种警告抑制经常出现在宏定义中。

另一种可能性是你已经发现的,要special-case空展开。但我更喜欢显式警告抑制,因为它消除了(虚假的)警告而不会使宏复杂化。您介绍的特殊情况实际上 不需要 - 没有它会生成完全相同的代码。