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

struct Centimeters(i32);

我想用Centimeters做基本的算术,而不是每次用模式匹配提取它们的"内部"值,也不需要实现AddSub...特性和重载运算符.

我想做的是:

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;
}

playpen

我在宏中模拟了正常的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 的内部值.

Rust相关问答推荐

如何定义使用拥有的字符串并返回拥有的Split的Rust函数?

即使参数和结果具有相同类型,fn的TypId也会不同

如果A == B,则将Rc A下推到Rc B

在执行其他工作的同时,从共享裁判后面的VEC中删除重复项

如何格式化传入Rust中mysql crate的Pool::new的字符串

在使用AWS SDK for Rust时,如何使用硬编码访问密钥ID和密钥凭据?

如何在递归数据 struct 中移动所有权时变异引用?

关于 map 闭合求和的问题

如何修复&q;无法返回引用函数参数的值在异步规则中返回引用当前函数&q;拥有的数据的值?

RUST 中的读写器锁定模式

tokio::spawn 有和没有异步块

需要一个有序向量来进行 struct 初始化

相当于 Rust 中 C++ 的 std::istringstream

是否可以通过可变引用推进可变切片?

在给定 Rust 谓词的情况下,将 Some 转换为 None 的惯用方法是什么?

&str 的编译时拆分是否可能?

第 7.4 章片段中如何定义 `thread_rng`

如何从 Rust 中不同类型的多个部分加入 Path?

为什么 Rust 标准库同时为 Thing 和 &Thing 实现特征?

为什么当borrow 变量发生变化时,borrow 变量不会改变?