Is it considered a bad practice to implement Deref for newtypes?

Viewed 11719

I often use the newtype pattern, but I am tired of writing my_type.0.call_to_whatever(...). I am tempted to implement the Deref trait because it permits writing simpler code since I can use my newtype as if it were the underlying type in some situations, e.g.:

use std::ops::Deref;

type Underlying = [i32; 256];
struct MyArray(Underlying);

impl Deref for MyArray {
    type Target = Underlying;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn main() {
    let my_array = MyArray([0; 256]);

    println!("{}", my_array[0]); // I can use my_array just like a regular array
}

Is this a good or bad practice? Why? What can be the downsides?

2 Answers

Contrary to the accepted answer, I found out that some popular crates implement Deref for types which are newtypes and aren't smart pointers:

  1. actix_web::web::Json<T> is a tuple struct of (T,) and it implements Deref<Target=T>.

  2. bstr::BString has one field typed Vec<u8> and it implements Deref<Target=Vec<u8>>.

So, maybe it's fine as long as it's not abused, e.g. to simulate multi-level inheritance hierarchies. I also noticed that the two examples above have either zero public methods or only one into_inner method which returns the inner value. It seems then a good idea to keep the number of methods of a wrapper type minimal.

Related