为Rust newtypes自动实现封闭类型的特征(带有一个字段的元组结构)

时间:2014-07-18 18:30:15

标签: struct traits rust newtype

在Rust中,可以创建只有一个字段的元组结构,如下所示:

struct Centimeters(i32);

我希望使用Centimeters进行基本算术,而不是每次使用模式匹配提取其“内部”值,并且不执行AddSub,...特征和重载运算符。

我想做的是:

let a = Centimeters(100);
let b = Centimeters(200);
assert_eq!(a + a, b);

3 个答案:

答案 0 :(得分:18)

  

有没有办法在没有每次使用模式匹配时提取它们的“内部”值,而没有实现Add,Sub,... traits和重载运算符?

不,唯一的方法是手动实现特征。 Rust没有等价于the Haskell's GHC extension GeneralizedNewtypeDeriving,它允许包装类型上的deriving自动实现包装类型实现的任何类型类/特征(以及当前设置的Rust {{1}作为一个简单的AST转换,像Haskell一样实现它基本上是不可能的。)

要缩短流程,您可以使用宏:

#[derive]

playpen

我在宏中模拟了正常的use std::ops::{Add, Sub}; macro_rules! obvious_impl { (impl $trait_: ident for $type_: ident { fn $method: ident }) => { impl $trait_<$type_> for $type_ { type Output = $type_; fn $method(self, $type_(b): $type_) -> $type_ { let $type_(a) = self; $type_(a.$method(&b)) } } } } #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)] pub struct Centimeters(i32); obvious_impl! { impl Add for Centimeters { fn add } } obvious_impl! { impl Sub for Centimeters { fn sub } } #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)] pub struct Inches(i32); obvious_impl! { impl Add for Inches { fn add } } obvious_impl! { impl Sub for Inches { fn sub } } fn main() { let a = Centimeters(100); let b = Centimeters(200); let c = Inches(10); let d = Inches(20); println!("{:?} {:?}", a + b, c + d); // Centimeters(300) Inches(30) // error: // a + c; } 语法,通过查看宏调用(即减少查看宏定义的需要)来明确发生的事情,并保持Rust的自然可搜索性:如果您正在寻找impl上的特征,只需查看Centimeters的grep,您就会发现这些宏调用以及普通的for Centimeters

如果您经常访问impl类型的内容,可以考虑使用带字段的正确结构来定义包装器:

Centimeters

这允许您编写struct Centimeters { amt: i32 } 而不必进行模式匹配。您还可以定义类似self.amt的函数,称为fn cm(x: i32) -> Centimeters { Centimeters { amt: x } },以避免构造完整结构的详细程度。

您还可以使用cm(100).0语法访问元组结构的内部值。

答案 1 :(得分:8)

我为此问题制作了derive_more crate。它可以为元素实现它们的结构派生出许多特征。

您需要将derive_more添加到Cargo.toml。然后你可以写:

#[macro_use]
extern crate derive_more;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Add)]
struct Centimeters(i32);

fn main() {
    let a = Centimeters(100);
    let b = Centimeters(200);
    assert_eq!(a + a, b);
}

答案 2 :(得分:-1)

对于Rust版本1.10.0,我觉得类型别名完全符合您所描述的情况。他们只是给一个类型一个不同的名字。

我们说所有厘米都是u32 s。然后我可以使用代码

type Centimeters = u32;

u32拥有的任何特征,Centimeters都会自动拥有。这并没有消除将Centimeters添加到Inches的可能性。如果你小心,你就不需要不同的类型。