自动实现 Rust 新类型的封闭类型特征(具有一个字段的元组结构) [英] Automatically implement traits of enclosed type for Rust newtypes (tuple structs with one field)

查看:39
本文介绍了自动实现 Rust 新类型的封闭类型特征(具有一个字段的元组结构)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Rust 中,可以像下面这样创建只有一个字段的元组结构:

In Rust, tuple structs with only one field can be created like the following:

struct Centimeters(i32);

我想用 Centimeters 做基本的算术运算,而不用每次都通过模式匹配提取它们的内部"值,也不需要实现 Add, Sub, ... trait 和重载运算符.

I want to do basic arithmetic with Centimeters without extracting their "inner" values every time with pattern matching, and without implementing the Add, Sub, ... traits and overloading operators.

我想做的是:

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

推荐答案

有没有办法在不每次使用模式匹配时提取它们的内部"值,并且不实现 Add、Sub、...traits 和重载运算符的情况下做到这一点?

is there a way to do it without extracting their "inner" values every time with pattern matching, and without implementing the Add, Sub, ... traits and overloading operators?

不,唯一的方法是手动实现特征.Rust 没有对应于 Haskell 的 GHC 扩展 GeneralizedNewtypeDeriving 允许包装类型的 deriving 自动实现包装类型实现的任何类型类/特征(以及当前的设置Rust 的 #[derive] 作为一个简单的 AST 转换,像 Haskell 那样实现它基本上是不可能的.)

No, the only way is to implement the traits manually. Rust doesn't have an equivalent to the Haskell's GHC extension GeneralizedNewtypeDeriving which allows deriving on wrapper types to automatically implement any type class/trait that the wrapped type implements (and with the current set-up of Rust's #[derive] as a simple AST transformation, implementing it like Haskell is essentially impossible.)

为了简化流程,您可以使用宏:

To abbreviate the process, you could use a macro:

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 并且您会发现这些宏调用以及正常的 impls.

I emulated the normal impl syntax in the macro to make it obvious what is happening just by looking at the macro invocation (i.e. reducing the need to look at the macro definition), and also to maintain Rust's natural searchability: if you're looking for traits on Centimeters just grep for for Centimeters and you'll find these macro invocations along with the normal impls.

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

If you are accessing the contents of the Centimeters type a lot, you could consider using a proper struct with a field to define the wrapper:

struct Centimeters { amt: i32 }

这允许您编写 self.amt 而不必进行模式匹配.您还可以定义一个函数,如 fn cm(x: i32) ->厘米 { 厘米 { amt: x } },类似于 cm(100) 的调用,以避免构建完整结构的冗长.

This allows you to write self.amt instead of having to do the pattern matching. You can also define a function like fn cm(x: i32) -> Centimeters { Centimeters { amt: x } }, called like cm(100), to avoid the verbosity of constructing a full struct.

您还可以使用 .0.1 语法访问元组结构的内部值.

You can also access the inner values of a tuple struct using the .0, .1 syntax.

这篇关于自动实现 Rust 新类型的封闭类型特征(具有一个字段的元组结构)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆