> If you want to interact with your newtypes, you need to either unwrap it or reimplement each typeclass/trait
...or you could just e.g. implement Deref in Rust? In my experience that solves almost all use cases (with the edge case being when something wants to take ownership of the wrapped value, at which point I don't see the problem with unwrapping)
That gets us halfway there. It makes unwrapping easy, but you still need to remember to rewrap if you've implemented anything.
use std::ops::Deref;
trait Test {
fn test(&self);
}
#[derive(Debug)]
struct Wrap<T>(T);
impl<T> Test for Wrap<T> {
fn test(&self) {
()
}
}
impl<T> Deref for Wrap<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let thing1 = Wrap(3_i32);
let thing2 = Wrap(5_i32);
let sum = *thing1 + *thing2;
thing1.test();
thing2.test();
sum.test(); // error[E0599]: no method named `test` found for type `i32` in the current scope
}
Also using newtypes to reimplement methods on the base type is frowned upon. I believe that this is why #[derive(Deref)] isn't included in the standard library. See below (emphasis mine):
> So, as a simple, first-order takeaway: if the wrapper is a trivial marker, then it can implement Deref. If the wrapper's entire purpose is to manage its inner type, without modifying the extant semantics of that type, it should implement Deref. If T behaves differently than Target when Target would compile with that usage, it shouldn't implement Deref.
...or you could just e.g. implement Deref in Rust? In my experience that solves almost all use cases (with the edge case being when something wants to take ownership of the wrapped value, at which point I don't see the problem with unwrapping)