Debug and Copy for HashMap inside `Cell`

Following up on my question at Architecture Suggestions for Idiomatic Rust, I am using interior mutability with both a Cell and a RefCell on the same struct. I have this working.

#[derive(Debug)]
struct Token {
    id: String,
    class: String,
    storage: RefCell<Vec<u32>>,
    state: Cell<u32>,
}

When the type inside Cell or RefCell is a primitive (like u32), I can derive Debug just fine. But, when I want Cell to hold a HashMap<String, String>, I get an error:

error[E0277]: the trait boundstd::iter::Map<std::string::String, std::string::String>: std::marker::Copyis not satisfied.

I saw from the post at Why is `Cell` not `Copy`? that Cell does not implement Copy and that makes sense.

So, is there a way to derive Debug on an interior mutable struct?

Also, why does this work with primitives like u32? If it is Cell that does not implement Copy, why would it matter what is inside the Cell?

Yes, by using any interior mutability type besides Cell. Cell's gimmick is that it’s impossible to borrow what’s inside, meaning you can’t call trait methods that need to borrow what’s inside. You won’t run into this problem with RefCell or RwLock, which have different gimmicks.

@ExpHP Thank you. I suppose I could use RefCell instead of Cell here. I don’t totally understand the difference, but from what I read in the book, either should work for this.

storage is a shared vector that is to be updated by any references, and through other methods, I can guarantee not at the same time.

state is similar except that it won’t be updated, just replaced when needed.

This may be how you’re using it… but if you want Debug to work, then borrows are needed.

Let’s consider what happens if you try to implement Debug without the T: Copy requirement:

impl<T: fmt::Debug> fmt::Debug for Cell<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let inner = ?????;
        fmt::Debug::fmt(inner, f)
    }
}

fmt::Debug::fmt's first argument is &self, so inner must have type &T. However, it is impossible to extract a &T from a Cell. That’s why the impl has a Copy requirement:

impl<T: fmt::Debug + Copy> fmt::Debug for Cell<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let inner = self.get();
        fmt::Debug::fmt(&inner, f)
    }
}

I mean, they could have tried the rather heavy duty solution of briefly removing the item from the cell:

impl<T: fmt::Debug + Default> fmt::Debug for Cell<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let inner = self.replace(Default::default());
        fmt::Debug::fmt(&inner, f)?;
        self.set(inner);
        Ok(())
    }
}

But the fact that the Cell’s contents was temporarily removed can be observed by outside code; in particular, T could contain a &Cell that points to the same cell, which it can observe in the call to <T as fmt::Debug>::fmt.

Wow, that makes great sense. Thinking through that, I see no need to use Cell in this case. RefCell should work just as well.