簡體   English   中英

如何在 Rust 中為值和引用實現慣用的運算符重載?

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

在實現基本的固定大小向量類型(例如float2 )時,我想支持AddSub特征。 稍后,我將要支持Mul*Assign

查找文檔和其他示例,我想出了這個:

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

這適用於基本示例,但是我發現在實踐中我經常以引用作為參數以及堆棧上的本地float2的引用結束。

要混合這些,我需要:

  • 取消引用變量(好的,但會使代碼的可讀性稍差)。
  • 也聲明引用的運算符重載組合。

例子:

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 */

雖然這允許在不取消引用的情況下編寫表達式。 枚舉每個組合變得非常乏味,尤其是在添加更多操作和類型( float3float4 ...)時。

有沒有普遍接受的方法來...

  • 為運算符重載自動強制類型?
  • 使用宏或語言的其他一些特性來避免繁瑣的重復?

或者是否期望開發人員:

  • 根據需要顯式訪問變量作為引用。
  • 根據需要顯式取消引用變量。
  • 寫了很多重復的操作符重載函數。

請注意,我目前是一名初學者,我在 Rust 中檢查了一些非常高級的數學庫,它們超出了我的腦海,而我可以使用它們 - 我想了解如何為我自己的類型編寫運算符重載。

Rust 的偉大之處在於它是開源的。 這意味着您可以看到該語言的作者如何解決問題。 最接近的模擬是原始整數類型

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定義為

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

為引用編寫特征的包裝器實現當然是有效的,這些引用只是取消引用並調用面向值的版本。

我建議您為此目的使用impl_os crate,請參閱我寫的其他答案

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM