Puzzling macro errors

I prototyped a macro for a crate I’ve been working on, pidgin. Pidgin facilitates constructing vanilla regexes instrumented and wrapped to provide parse trees for non-recursive grammars. Right now it has a procedural interface which is pretty opaque. I wanted to make a macro so you could do this:

let g = grammar!{
  TOP => <foo> | <bar>
  bar => ("cat") | ("camel")
  foo => ("dog") | ("dromedary")

The prototype works. The code.

pidgin-tester (master #) $ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/pidgin-tester`

larry := [((?), [G("bob", Some(5), Some(5), false)]), ((?), [G("bar", Some(3), None, false)]), ((?a), [S("this", None, Some(1), false), G("ba
r", Some(0), None, false), S("that", None, None, false), G("bob", Some(1), None, false)])]
bob := [((?), [R("foo")])]
bar := [((?i), [V(["baz", "plugh"], None, None, false)])]

foo := [((?), [S("bar", None, None, false)])]
pidgin-tester (master #) $

However, when I tried sticking it in pidgin in src/macros.rs I started getting double borrowing errors. Also, if I paste it into the playground and try to run it there I get the same errors. I don’t understand what differentiates these contexts such that different borrowing is happening. Here is a sample error:

   Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `m` as mutable more than once at a time
   --> src/main.rs:18:28
18  |           grammar!(@rules l, m, $($rest)+ );
    |                              ^ mutable borrow starts here in previous iteration of loop
19  |           PseudoGrammar::new(l, m, $mflags)
20  |       });
    |       - mutable borrow ends here
274 |       let g = grammar!{
    |  _____________-
275 | |         (?bBw)
276 | |         larry => <bob>{5} | <bar>{3,}
277 | |         larry -> ("this")? <bar>* ("that") <bob>+
278 | |         bob   => r("foo")
279 | |         bar   => (?i) [&v]
280 | |     };
    | |_____- in this macro invocation

I’m a rust newbie. This is my first macro (and it’s a bit of a doozie). Any help would be appreciated.

It works with the 2018 edition, not with the 2015 edition due to non-lexical lifetimes.

I really, really wish cargo hadn’t changed behaviour to silently default to 2018. I’ve lost count of the number of times it’s screwed people over lately. You need to go into Cargo.toml and delete the edition = "2018" line.

You can fix this by enclosing the borrows of m in a block:

    // the general way to start a new rule
    (@start_rule $l:expr, $m:expr, $name:ident, $mf:expr, $($rest:tt)+) => (
        let name = stringify!($name).to_string();
        if !$m.contains_key(&name) {
            let alternates = $m.entry(name).or_insert_with(|| Vec::new());
            let v: Vec<Part>= Vec::new();
            alternates.push(($mf, v));
        grammar!(@rules $l, $m, $($rest)+)

Thanks! I actually had to add edition = "2018" to Cargo.toml and also enclose those lines in a block, but then everything is copacetic. You’ve saved me a lot of grief.