How to properly use newtypes to "extend" a struct

I have a newtype that will only exist when a certain feature is enabled. That newtype essentially extends another struct by changing exactly one method of that struct. Now I am accomplishing this by just having the body of the newtype methods all simply call the methods of the original struct except for the single method I'm changing.

struct Foo { foo: u8 };
impl Bar for Foo {
    fn bar(&self) -> u8 {
        self.foo
    }

    fn bar2(&self) -> u8 {
        2u8 * self.foo
    }
}

struct Baz(Foo);
impl Bar for Baz {
    fn bar(&self) -> u8 {
        self.0.bar()
    }

    fn bar2(&self) -> u8 {
        self.0.foo.pow(2u32)
    }
}

Is this the correct way to use newtypes or am I getting too deeply entrenched in an OOP inheritance mindset?

That's the correct way to do it if you intend to keep the inner type tightly controlled.

If you don't care, you can implement Deref that will automatically expose it. It can be abused for inheritance-like behavior, but it's considered a bad style.

If you just want to add a method to get extra functionality for convenience, then use a trait instead.

2 Likes

I don't think I can just use a trait in this case because the actual code requires the method from the current trait be exposed in another module.

Thanks for clarifying about Deref. I hadn't considered that but I can see the temptation.

https://doc.rust-lang.org/std/ops/trait.Deref.html

Implementing Deref for smart pointers makes accessing the data behind them convenient, which is why they implement Deref . On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.

For example the Pin type relies on this semantic. It doesn't make sense to allow Pin<Newtype<T>> while moving it around actually moves the T out.