#Rust Macro Exercise Issue.

3 messages · Page 1 of 1 (latest)

daring glen
#

For the Rust Macro exercise I have written the following code:

macro_rules! hashmap {
    () => {
        std::collections::HashMap::new()
    };
    ($($k:literal $(=>)+ $v:expr),*$(,)?) => {
        {   
            use std::collections::HashMap;
            let mut hm = HashMap::new();
            {
                $(
                    hm.insert($k,$v);
                )*
            }
            hm
        }
    };
} ```

When I run it on the online editor I get the following error:

```Compiling macros v0.1.0 (/mnt/exercism-iteration)
error[E0308]: mismatched types
   --> tests/macros.rs:112:55
    |
112 |     let _empty: ::std::collections::HashMap<(), ()> = hashmap!();
    |                 -----------------------------------   ^^^^^^^^^^ expected `HashMap<(), ()>`, found `HashMap`
    |                 |
    |                 expected due to this
    |
    = note: `HashMap` and `std::collections::HashMap<(), ()>` have similar names, but are actually distinct types
note: `HashMap` is defined in the current crate
   --> tests/macros.rs:96:13
    |
96  |             pub struct HashMap;
    |             ^^^^^^^^^^^^^^^^^^
note: `std::collections::HashMap<(), ()>` is defined in crate `std`
   --> /rustc/da6b55cc5eaf76ed6acb7dc2f7d611e32af7c9a7/library/std/src/collections/hash/map.rs:214:1
    = note: this error originates in the macro `hashmap` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0308`.
error: could not compile `macros` due to previous error
warning: build failed, waiting for other jobs to finish...  ```

I think I understand the error, however I can't think of how to correct my macro to get around this error. If I try to explicitly type cast things, then it runs afoul of the other tests. If I run the problem test on my local machine: 
```#[cfg(test)]
mod tests {
    #[test]
    fn type_not_in_scope() {
        use hashmap;
        let _empty: ::std::collections::HashMap<(), ()> = hashmap!();
        let _without_comma = hashmap!(23=> 623, 34 => 21);
        let _with_trailing = hashmap!(23 => 623, 34 => 21,);
    }
} ```

Then it passes this test
```running 1 test
test tests::type_not_in_scope ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s```.

Any suggestions would be much appreciated.
pulsar oxide
#

I think you're looking at the wrong test. This is most likely the test that fails:

#[test]
#[ignore]
fn type_override() {
    // The macro should always use std::collections::HashMap and ignore crate::std::collections::HashMap
    mod std {
        pub mod collections {
            pub struct HashMap;
            impl HashMap {
                #[allow(dead_code)]
                pub fn new() -> Self {
                    panic!("Do not allow users to override which HashMap is used");
                }
                #[allow(dead_code)]
                pub fn insert<K, V>(&mut self, _key: K, _val: V) {
                    panic!("Do not allow users to override which HashMap is used");
                }
            }
        }
    }
    let _empty: ::std::collections::HashMap<(), ()> = hashmap!();
    let _without_comma = hashmap!(1 => 2, 3 => 4);
    let _with_trailing = hashmap!(1 => 2, 3 => 4,);
}

The cause of the error is subtle. In your code, you use std::collections::HashMap - but what if someone was to declare an inner namespace named std::collections with a class named HashMap? Granted, this is weird, but to prevent such a problem, macros should always refer to the type from the namespace root:

let mut h = ::std::collections::HashMap::new();
// and not:
// let mut h = std::collections::HashMap::new();

That extra :: at the start of the type declaration will ensure you always pick the type from std and not some inner namespace.

daring glen
#

Thank you so much!! This was driving me crazy, but you are exactly right!