1

I noticed that Box<T> implements everything that T implements and can be used transparently. For Example:

let mut x: Box<Vec<u8>> = Box::new(Vec::new());
x.push(5);

I would like to be able to do the same.

This is one use case:

Imagine I'm writing functions that operate using an axis X and an axis Y. I'm using values to change those axis that are of type numbers but belongs only to one or the other axis.

I would like my compiler to fail if I attempt to do operations with values that doesn't belong to the good axis.

Example:

let x = AxisX(5);
let y = AxisY(3);
let result = x + y; // error: incompatible types

I can do this by making a struct that will wrap the numbers:

struct AxisX(i32);
struct AxisY(i32);

But that won't give me access to all the methods that i32 provides like abs(). Example:

x.abs() + 3 // error: abs() does not exist
// ...maybe another error because I don't implement the addition...

Another possible use case:

You can appropriate yourself a struct of another library and implement or derive anything more you would want. For example: a struct that doesn't derive Debug could be wrapped and add the implementation for Debug.

Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69
Cecile
  • 1,553
  • 1
  • 15
  • 26
  • 1
    [`std::ops::Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) and its mutable equivalent [`std::ops::DerefMut`](https://doc.rust-lang.org/std/ops/trait.DerefMut.html) – CodesInChaos Jun 03 '18 at 10:20
  • 1
    You should be more careful when approving edits. The Edit from iPhoton introduced more errors than it fixed. – Tim Diekmann Jun 03 '18 at 16:02
  • I thought my English was just bad ^_^ I'm not a native english speaker – Cecile Jun 04 '18 at 16:46
  • Somehow related article: https://yoric.github.io/post/uom.rs/ – Cecile Jan 27 '20 at 14:06

1 Answers1

2

You are looking for std::ops::Deref:

In addition to being used for explicit dereferencing operations with the (unary) * operator in immutable contexts, Deref is also used implicitly by the compiler in many circumstances. This mechanism is called 'Deref coercion'. In mutable contexts, DerefMut is used.

Further:

If T implements Deref<Target = U>, and x is a value of type T, then:

  • In immutable contexts, *x on non-pointer types is equivalent to *Deref::deref(&x).
  • Values of type &T are coerced to values of type &U
  • T implicitly implements all the (immutable) methods of the type U.

For more details, visit the chapter in The Rust Programming Language as well as the reference sections on the dereference operator, method resolution and type coercions.

By implementing Deref it will work:

impl Deref for AxisX {
    type Target = i32;

    fn deref(&self) -> &i32 {
        &self.0
    }
}

x.abs() + 3

You can see this in action on the Playground.

However, if you call functions from your underlying type (i32 in this case), the return type will remain the underlying type. Therefore

assert_eq!(AxisX(10).abs() + AxisY(20).abs(), 30);

will pass. To solve this, you may overwrite some of those methods you need:

impl AxisX {
    pub fn abs(&self) -> Self {
        // *self gets you `AxisX`
        // **self dereferences to i32
        AxisX((**self).abs())
    }
}

With this, the above code fails. Take a look at it in action.

Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69
  • Unfortunately it doesn't work as good as I thought. I made a struct AxisX and AxisY but the result of any method is i32 so I'm allowed to do stuff I shouldn't: https://play.rust-lang.org/?gist=d7975e0d6cc29274280eedb340ba122d&version=stable&mode=debug – Cecile Jun 03 '18 at 15:31
  • 1
    @Cecile Well, you have to decide if you want to use all methods of `i32`, or if you want the types to be completely distinct. Of course it is still possible to implement the methods like `abs()` itself. I have expanded my answer to that. – Tim Diekmann Jun 03 '18 at 15:46
  • 2
    @Cecile [Is it considered a bad practice to implement Deref for newtypes?](https://stackoverflow.com/q/45086595/155423). – Shepmaster Jun 03 '18 at 16:17
  • In the end I just don't use it at all because it doesn't ensure the types without making wrapper functions of everything. The cost of the implementation (without deref) is too high for the use I have. – Cecile Jun 04 '18 at 16:48