在Rust中,可以创建只有一个字段的元组结构,如下所示:
struct Centimeters(i32);
我希望使用Centimeters
进行基本算术,而不是每次使用模式匹配提取其“内部”值,并且不执行Add
,Sub
,...特征和重载运算符。
我想做的是:
let a = Centimeters(100);
let b = Centimeters(200);
assert_eq!(a + a, b);
答案 0 :(得分:18)
有没有办法在没有每次使用模式匹配时提取它们的“内部”值,而没有实现Add,Sub,... traits和重载运算符?
不,唯一的方法是手动实现特征。 Rust没有等价于the Haskell's GHC extension GeneralizedNewtypeDeriving
,它允许包装类型上的deriving
自动实现包装类型实现的任何类型类/特征(以及当前设置的Rust {{1}作为一个简单的AST转换,像Haskell一样实现它基本上是不可能的。)
要缩短流程,您可以使用宏:
#[derive]
我在宏中模拟了正常的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
的可能性。如果你小心,你就不需要不同的类型。