简体   繁体   English

如何在 Rust 中为值和引用实现惯用的运算符重载?

[英]How to implement idiomatic operator overloading for values and references in Rust?

When implementing a primitive fixed-size vector type ( float2 for example), I want to support the Add and Sub traits.在实现基本的固定大小向量类型(例如float2 )时,我想支持AddSub特征。 Later, I will want to support Mul and *Assign .稍后,我将要支持Mul*Assign

Looking up the documentation and other examples, I came up with this:查找文档和其他示例,我想出了这个:

use std::ops::{Add, Sub};

#[derive(Copy, Clone)]
struct float2(f64, f64);

impl Add for float2 {
    type Output = float2;
    fn add(self, _rhs: float2) -> float2 {
        float2(self.0 + _rhs.0, self.1 + _rhs.1)
    }
}

impl Sub for float2 {
    type Output = float2;
    fn sub(self, _rhs: float2) -> float2 {
        float2(self.0 - _rhs.0, self.1 - _rhs.1)
    }
}

This works for basic examples, however I found in practice I would often end up with references passed in as arguments as well as local float2 's on the stack.这适用于基本示例,但是我发现在实践中我经常以引用作为参数以及堆栈上的本地float2的引用结束。

To mix these I needed to either:要混合这些,我需要:

  • De-reference variables (OK but makes code a little less readable).取消引用变量(好的,但会使代码的可读性稍差)。
  • Declare operator overloading combinations of references too.也声明引用的运算符重载组合。

Example:例子:

impl<'a, 'b> Add<&'b float2> for &'a float2 {
    type Output = float2;
    fn add(self, _rhs: &'b float2) -> float2 {
        float2(self.0 + _rhs.0, self.1 + _rhs.1)
    }
}
impl<'a> Add<float2> for &'a float2 {
    type Output = float2;
    fn add(self, _rhs: float2) -> float2 {
        float2(self.0 + _rhs.0, self.1 + _rhs.1)
    }
}
impl<'b> Add<&'b float2> for float2 {
    type Output = float2;
    fn add(self, _rhs: &'b float2) -> float2 {
        float2(self.0 + _rhs.0, self.1 + _rhs.1)
    }
}

/*... and again for Sub */

While this allows to write expressions without de-referencing.虽然这允许在不取消引用的情况下编写表达式。 it becomes quite tedious to enumerate each combinations, especially when adding more operations & types ( float3 , float4 ...).枚举每个组合变得非常乏味,尤其是在添加更多操作和类型( float3float4 ...)时。

Is there a generally accepted way to...有没有普遍接受的方法来...

  • Automatically coerce types for operator overloading?为运算符重载自动强制类型?
  • Use macros or some other feature of the language to avoid tedious repetition?使用宏或语言的其他一些特性来避免繁琐的重复?

Or is it expected that developers either:或者是否期望开发人员:

  • Explicitly access variables as references as needed.根据需要显式访问变量作为引用。
  • Explicitly de-reference variables as needed.根据需要显式取消引用变量。
  • Write a lot of repetitive operator overloading functions.写了很多重复的操作符重载函数。

Note, I'm currently a beginner, I've checked some quite advanced math libraries in Rust, they're way over my head, while I could use them - I would like to understand how to write operator overloading for my own types.请注意,我目前是一名初学者,我在 Rust 中检查了一些非常高级的数学库,它们超出了我的脑海,而我可以使用它们 - 我想了解如何为我自己的类型编写运算符重载。

The great thing about Rust is that it's open source. Rust 的伟大之处在于它是开源的。 This means you can see how the authors of the language have solved a problem.这意味着您可以看到该语言的作者如何解决问题。 The closest analogue is primitive integer types :最接近的模拟是原始整数类型

macro_rules! add_impl {
    ($($t:ty)*) => ($(
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Add for $t {
            type Output = $t;

            #[inline]
            fn add(self, other: $t) -> $t { self + other }
        }

        forward_ref_binop! { impl Add, add for $t, $t }
    )*)
}

forward_ref_binop is defined as : forward_ref_binop定义为

macro_rules! forward_ref_binop {
    (impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl<'a> $imp<$u> for &'a $t {
            type Output = <$t as $imp<$u>>::Output;

            #[inline]
            fn $method(self, other: $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(*self, other)
            }
        }

        #[stable(feature = "rust1", since = "1.0.0")]
        impl<'a> $imp<&'a $u> for $t {
            type Output = <$t as $imp<$u>>::Output;

            #[inline]
            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(self, *other)
            }
        }

        #[stable(feature = "rust1", since = "1.0.0")]
        impl<'a, 'b> $imp<&'a $u> for &'b $t {
            type Output = <$t as $imp<$u>>::Output;

            #[inline]
            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(*self, *other)
            }
        }
    }
}

It's certainly valid to write wrapper implementations of the traits for references that simply dereference and call the value-oriented version.为引用编写特征的包装器实现当然是有效的,这些引用只是取消引用并调用面向值的版本。

我建议您为此目的使用impl_os crate,请参阅我写的其他答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM