在Rust中,可以创建只有一个字段的元组 struct ,如下所示:
struct Centimeters(i32);
我想用Centimeters
做基本的算术,而不是每次用模式匹配提取它们的"内部"值,也不需要实现Add
、Sub
...特性和重载运算符.
我想做的是:
let a = Centimeters(100);
let b = Centimeters(200);
assert_eq!(a + a, b);
在Rust中,可以创建只有一个字段的元组 struct ,如下所示:
struct Centimeters(i32);
我想用Centimeters
做基本的算术,而不是每次用模式匹配提取它们的"内部"值,也不需要实现Add
、Sub
...特性和重载运算符.
我想做的是:
let a = Centimeters(100);
let b = Centimeters(200);
assert_eq!(a + a, b);
有没有一种方法可以做到这一点,而不用每次通过模式匹配提取它们的"内部"值,也不用实现Add、Sub...特征和重载操作符?
不,唯一的方法是手动实现这些特性.Rust没有the Haskell's GHC extension GeneralizedNewtypeDeriving
的类似功能,它允许包装类型上的deriving
自动实现包装类型实现的任何类型类/特征(而当前Rust的#[derive]
设置为一个简单的AST转换,像Haskell一样实现它基本上是不可能的)
为了简化流程,可以使用宏:
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;
}
我在宏中模拟了正常的impl
语法,通过查看宏调用(即减少查看宏定义的需要),可以清楚地看到正在发生的事情,此外,为了保持Rust的自然搜索能力:如果你在Centimeters
上寻找特征,只需grep for for Centimeters
,你就能找到这些宏调用以及正常的impl
.
如果您正在访问Centimeters
类型的内容很多,您可以考虑使用一个适当的 struct 来定义包装:
struct Centimeters { amt: i32 }
这允许您写入self.amt
,而不必进行模式匹配.您还可以定义像fn cm(x: i32) -> Centimeters { Centimeters { amt: x } }
这样的函数,称为like cm(100)
,以避免构建完整 struct 的冗长.
还可以使用.0
.1
语法访问元组 struct 的内部值.