如何在 Rust 中为值和引用实现惯用的运算符重载? [英] How to implement idiomatic operator overloading for values and references in Rust?
问题描述
在实现基本的固定大小向量类型(例如float2
)时,我希望支持Add
和Sub
特性.稍后,我会想要支持Mul
和*Assign
.
When implementing a primitive fixed-size vector type (float2
for example), I want to support the Add
and Sub
traits. Later, I will want to support Mul
and *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)
}
}
这适用于基本示例,但是我发现在实践中我经常以引用作为参数以及堆栈上的本地 float2
的引用结束.
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.
要混合这些,我需要:
- 取消引用变量(可以,但会使代码可读性稍差).
- 也声明运算符重载引用组合.
示例:
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 */
虽然这允许在不取消引用的情况下编写表达式.枚举每个组合变得非常乏味,尤其是在添加更多操作时类型(float3
、float4
...).
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
...).
是否有一种普遍接受的方式来...
Is there a generally accepted way to...
- 为运算符重载自动强制类型?
- 使用宏或语言的其他一些特性来避免繁琐的重复?
或者希望开发人员:
- 根据需要显式访问变量作为引用.
- 根据需要显式取消引用变量.
- 编写大量重复的运算符重载函数.
注意,我目前是初学者,我已经检查了 Rust 中的一些非常高级的数学库,它们让我无法理解,而我可以使用它们 - 我想了解如何编写运算符重载我自己的类型.
推荐答案
Rust 的伟大之处在于它是开源的.这意味着您可以看到该语言的作者如何解决问题.最接近的类似物是 原始整数类型:
The great thing about Rust is that it's open source. 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 }
)*)
}
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.
这篇关于如何在 Rust 中为值和引用实现惯用的运算符重载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!