Prevent interior mutability?

I’m trying to create a wrapper type that ensures certain properties of the type it wraps by checking them during construction. Something like this:

trait HasProperty {
    fn has_property(&self) -> bool;
}

struct Property<P: HasProperty>(P);

impl<P: HasProperty> Property<P> {
    fn new(p: P) -> Option<Property<P>> {
        if p.has_property() {
            return Some(Property(p));
        }
        None
    }
}

This works fine, but then the question becomes: How do I get access to the P inside? The obvious answer is something like Deref:

impl<P: HasProperty> Deref for Property<P> {
    type Target = P;
    fn deref(&self) -> &P {
        &self.0
    }
}

However, what if P has interior mutability, like Cell or something like that? Then even an immutable reference isn’t sufficient to prevent somebody from modifying the value so that it no longer satisfies the property in question, which defeats the entire purpose of Property. Is there any way that I can ensure that P doesn’t have interior mutability?

Ate you going to use unsafe code that depends on this? If yes, then mark HasProperty as an unsafe trait. If you are not using unsafe code that depends on this, then just document that interior mutability can cause logic bugs. There is no way to rule out interior mutability.

If you are willing to dip into nightly, you can do this, and that will guarantee that no interior mutability takes place.

6 Likes

As a subtle counterexample, even a simple newtype of usize can theoretically be made to behave like Cell (although this pattern isn’t considered good unless it’s an interner or a gensymer.)

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5d69a8ddf5fdc21e4899e6dd5392cb47

2 Likes

I’ve never heard of gensymer, could you please explain what it is?

Ah sorry, you can forget about that. I’ve heard from someone about the gensym function in some Lisp dialects, and I’ve been (perhaps solely) using this word for referring something that generates unique ids. And I’ve listed gensym here only because it’s seen as a variant of interning. Anyway it isn’t the main point of the last post.

1 Like

OK, thanks for all the replies, folks! I take the point that it can’t be done. I’ll find another way :slight_smile: