Looking for help by implementing of Debug trait for HashMap

Unfortunately I'm unable to implement Debug trait by myself. Hence I'm looking for help here.

use std::{collections::HashMap, fmt::Debug};

lazy_static! {
    pub static ref REGION_LOCATION: HashMap<&'static str, Vec<&'static str>> = [
        ("de", vec!["fra", "txl"]),
        ("us", vec!["las", "ewr"]),
        ("gb", vec!["lhr"])
    ]
    .iter()
    .cloned()
    .collect();
}

impl std::fmt::Debug for REGION_LOCATION {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut inner: Vec<String> = Vec::with_capacity(REGION_LOCATION.len());
        for (key, val) in REGION_LOCATION.iter() {
            let l = val.join(", ");
            let item = format!("{{{}: [{}]}}", key, l).to_owned();
            inner.push(item.to_owned());
        }
        std::fmt::Debug::fmt(inner.as_ref(), f)
    }
}

The compiler asked for type annotatons, but I a newbie don't now how to do that. Could one more experienced developer help me to solve the issue?

error[E0283]: type annotations needed
  --> src/ionos/cloud/api/v5/locations.rs:22:9
   |
22 |         std::fmt::Debug::fmt(inner.as_ref(), f)
   |         ^^^^^^^^^^^^^^^^^^^^ -------------- this method call resolves to `&T`
   |         |
   |         cannot infer type
   |
   = note: cannot satisfy `_: Debug`
   = note: required by `std::fmt::Debug::fmt`

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0283`.

Thanks in advance!

First off, you don't implement a trait for a specific value. You implement it for a type, and it'll apply to all instances of that type. (That's kind of the whole point of having types: treating sets of values uniformly, independent of their actual run-time value.)

So, you'd want to impl Debug for HashMap<K, V> instead. But you can't, because:

  1. it's already implemented (look at the docs!), and
  2. even if it weren't implemented, you wouldn't be allowed to implement it, because neither HashMap nor Debug is defined by your code (so if any 3rd-party code could implement it, there would be bad problems – google "coherence" if you want to know the details).

Just make a newtype wrapper instead.

Technically implementing traits and associated methods works, since lazy_static already makes a newtype for every new lazy static.

1 Like

I can't decide if that's magnificiently clever or disgustingly surprising. Probably both.

1 Like

Does that mean there is nothing wrong in implementing impl std::fmt::Debug for REGION_LOCATION?

Yes, however, you're still not implementing the trait for the value; the macro apparently generates a type with the same name as the value. So there's a global static named REGION_LOCATION, and there's also a type called REGION_LOCATION. This is confusing because ALL_CAPS is not the convention for Rust types, but it's permitted.

Anyway, I just noticed that the "type annotations needed" compiler error is valid, too (as opposed to it being a red herring due to type checking going wrong somewhere else, as I initially thought). The issue there is that Vec::as_ref can be interpreted in two different ways. There's an impl AsRef<[T]> for Vec<T>, but there's also a blanket impl AsRef<T> for T. So the compiler can't decide which one to pick. You should probably use .as_slice() instead.

1 Like

Technically - nothing. You just have to remember that REGION_LOCATION here is not a map, but a singleton wrapper around this map. And, if you remove as_ref and just use &inner, this actually works - playground.

1 Like

You can debug-print the hashmap under the lazy_static with a single deref operator.

println!("{:?}", *REGION_LOCATION);

No need to manually impl the Debug trait.

5 Likes

The * operator is important. Here's a working example on Playground.

You could also implement Debug for the wrapper type by delegating to the HashMap impl:

impl std::fmt::Debug for REGION_LOCATION {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (**self).fmt(f)
    }
}

println!("{:?}", REGION_LOCATION);

(Playground)

4 Likes

In my humble opinion it is the most beautiful implementation.

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.