是否可以制作我自己的Box-like包装器?

时间:2018-06-03 05:33:15

标签: rust

我注意到Box<T>实现了T实现的所有内容,并且可以透明地使用。例如:

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

我希望能够做到这一点。

这是一个用例:

想象一下,我正在编写使用轴X和轴Y操作的函数。我使用值来更改那些类型为数字但仅属于一个轴或另一个轴的轴。

如果我尝试使用不属于良好轴的值进行操作,我希望我的编译器失败。

示例:

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

我可以通过创建一个包装数字的结构来实现这一点:

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

但这并不能让我访问i32提供的所有abs()方法。例如:

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

另一个可能的用例:

您可以自己使用另一个库的结构,并实现或派生您想要的任何内容。例如:一个不导出Debug的结构可以被包装并添加Debug的实现。

1 个答案:

答案 0 :(得分:2)

您正在寻找std::ops::Deref

  

除了用于在不可变上下文中使用(一元)*运算符进行显式解除引用操作之外,编译器在许多情况下也隐式使用Deref。这种机制称为“Deref coercion”。在可变的上下文中,使用了DerefMut

此外:

  

如果T实施Deref<Target = U>,而x的值为T,则:

     
      
  • 在不可变上下文中,非指针类型上的*x等同于*Deref::deref(&x)
  •   
  • 类型&T的值被强制转换为&U
  • 类型的值   
  • T隐式实现U类型的所有(不可变)方法。
  •   
     

有关详细信息,请访问the chapter in The Rust Programming Language以及the dereference operatormethod resolutiontype coercions上的参考部分。

通过实施Deref,它将起作用:

impl Deref for AxisX {
    type Target = i32;

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

x.abs() + 3

您可以在Playground上看到这一点。

但是,如果从基础类型调用函数(在这种情况下为i32),则返回类型将保留为基础类型。因此

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

将通过。要解决此问题,您可以覆盖所需的一些方法:

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

这样,上面的代码就失败了。 Take a look at it in action