gpt4 book ai didi

rust - 为移动和借用的所有组合实现 Add、Sub、Mul、Div

转载 作者:行者123 更新时间:2023-12-05 05:31:27 24 4
gpt4 key购买 nike

我有一个应该实现基本数学运算符的简单结构。最初我不想让这些消耗操作数,所以我实现了引用的特征,例如 Add。 :

impl<'a, 'b> Add<&'b MyType> for &'a MyType {
type Output = MyType;

fn add(self, rhs: &'b MyType) -> Self::Output {
// Implementation...
}
}

这让我可以:

let result = &v1 + &v2;

在哪里v1v2类型为 MyType .

然后我意识到有时候使用操作数在句法上更方便,例如:

let result = &v1 + &v2 + &v3;

因为有一个中间结果,上面的代码无法编译,你必须这样做:

let result = &v1 + &(&v2 + &v3);

所以我最终实现了移动和借用的其他排列,它们只是服从第一个排列:

impl<'a> Add<&'a MyType> for MyType {
type Output = MyType;

fn add(self, rhs: &'a MyType) -> Self::Output {
&self + rhs
}
}

impl<'a> Add<MyType> for &'a MyType {
type Output = MyType;

fn add(self, rhs: MyType) -> Self::Output {
self + &rhs
}
}

impl Add<MyType> for MyType {
type Output = MyType;

fn add(self, rhs: MyType) -> Self::Output {
&self + &rhs
}
}

这可行,但很麻烦。

我在寻找更简单的方法,例如使用 Borrow<T> :

impl<B> Add<B> for B
where
B: Borrow<MyType>,
{
type Output = MyType;

fn add(self, rhs: B) -> Self::Output {
// Implementation...
}
}

但是由于 type parameter 的缘故,这无法编译,这是可以理解的。 B must be used as the type parameter for some local type .

是否有任何其他技巧可以避免 Add 的所有这些样板实现?/Sub/Mul/Div等等?

更新:

@EvilTak 在评论中提出了一个建议,通过实现 B: Borrow<MyType> 来减少样板组合。 MyType 上的版本和 &MyType明确地。这是一个很好的改进:

impl<'a, B> Add<B> for &'a MyType
where
B: Borrow<MyType>,
{
type Output = MyType;

fn add(self, rhs: B) -> Self::Output {
// Implementation...
}
}

impl<B> Add<B> for MyType
where
B: Borrow<MyType>,
{
type Output = MyType;

fn add(self, rhs: B) -> Self::Output {
&self + rhs
}
}

最佳答案

进入这个兔子洞后,我将回答我自己的问题。

我开始使用 Borrow 来减少我需要实现的函数数量:

impl<'a, B> Add<B> for &'a MyType
where
B: Borrow<MyType>,
{
type Output = MyType;

fn add(self, rhs: B) -> Self::Output {
// Implementation...
}
}

impl<B> Add<B> for MyType
where
B: Borrow<MyType>,
{
type Output = MyType;

fn add(self, rhs: B) -> Self::Output {
&self + rhs
}
}

这很有效,直到我还需要添加 MyTypeMyOtherType在一起。

尝试使用 Borrow<T> 来实现它给出错误 conflicting implementations of trait :

impl<'a, B> Mul<B> for &'a MyType
where
B: Borrow<MyOtherType>,
{
type Output = MyOtherType;

fn mul(self, rhs: B) -> Self::Output {
/// Implementation...
}
}

这是因为类型 could theoretically implement both Borrow<MyType>Borrow<MyOtherType>同时,编译器不知道要使用哪个实现。

那时我决定改为尝试宏路线。如您所料,this has been done before by others .

几个不同的地方建议使用 impl_ops ,此后已被 auto_ops 取代.

这个 crate 允许您通过执行以下操作为运算符定义所有各种组合:

impl_op_ex!(+ |a: &DonkeyKong, b: &DonkeyKong| -> DonkeyKong { DonkeyKong::new(a.bananas + b.bananas) });

但是,这个箱子有不能使用泛型的限制。就我而言 MyType实际上是 Matrix<const M: usize, const N: usize> ,所以我需要泛型支持。

然后我遇到了 auto_impl_ops , 它可以让你从一个 AddAsign 生成所有不同的加法组合特征实现(和其他操作一样),并且还支持泛型。

use std::ops::*;
#
# #[derive(Clone, Default)]
# struct A<T>(T);

#[auto_impl_ops::auto_ops]
impl<M> AddAssign<&A<M>> for A<M>
where
for<'x> &'x M: Add<Output = M>,
{
fn add_assign(&mut self, other: &Self) {
self.0 = &self.0 + &other.0;
}
}

此处的一个限制是结果必须始终与 Self 的类型相同,如果您正在执行矩阵乘以向量等操作,情况可能并非如此。

另一个可能的问题是,对于二元运算符, crate 将在使用您的赋值运算符实现之前克隆左侧的值,并返回克隆。对于矩阵乘法,我还必须克隆 selfMulAssign实现,否则我将覆盖我仍在用于矩阵乘法的数据。这意味着这里至少有一个冗余内存副本,如果我手动实现运算符,我将不会有。

我现在已经使用了这个库。如果情况发生变化,我会尝试在此处进行更新。

关于rust - 为移动和借用的所有组合实现 Add、Sub、Mul、Div,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74363718/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com