带有引用的 Rust 泛型 AddAssign

Rust generic AddAssign with references

不久前,我研究了为斐波那契数列编写一个通用迭代器 可以接受原始数字和自定义类型(例如 bignums)。在未能获得适用于两种原始类型的版本之后 bignums,我偶然发现了这个问题:

使用所谓的Higher Ranked Trait Bounds来解决这个问题 具体问题。

但是现在,我正在尝试使用类似的策略来使用 *_assign 运营商代替。特别是,我正在尝试获得与此类似的东西 工作:

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

fn add_test<'a, T>(x: &'a T, y: &'a T) -> T
where
    for<'b> &'b T: Add<Output = T>,
{
    x + y
}

fn add_assign_test<'a, T>(x: &'a mut T, y: &'a T) -> T
where
    for<'b> &'b mut T: AddAssign<&'b T>,
    T: Clone,
{
    x += y;
    x.clone()
}

fn main() {
    println!("add_test()={}", add_test(&1, &2));
    println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
}

add_test() 按预期工作,但我无法让 add_assign_test() 以类似的方式工作。我得到的错误表明可能实际上不存在针对原始类型的这种行为的实现:

error[E0277]: the trait bound `for<'b> &'b mut _: std::ops::AddAssign<&'b _>` is not satisfied
  --> src/main.rs:21:38
   |
21 |     println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
   |                                      ^^^^^^^^^^^^^^^ no implementation for `&'b mut _ += &'b _`
   |
   = help: the trait `for<'b> std::ops::AddAssign<&'b _>` is not implemented for `&'b mut _`
   = note: required by `add_assign_test`

我可以创建一个宏来为这些实际引用原始类型的运算符创建实现,但这似乎有点浪费。还有其他方法可以达到同样的效果吗?

只是您的代码中的一个小疏忽。让我们看看特征:

pub trait AddAssign<Rhs = Self> {
    fn add_assign(&mut self, rhs: Rhs);
}

该方法的接收者已经是 &mut self 而不是 self。你必须用 Add 做额外工作的原因是因为它接受 self 作为接收者。对于 AddAssign 这意味着:如果类型 T 实现了 AddAssign,您可以在 &mut T!

上调用方法 add_assign()

因此,而不是写:

where for <'b> &'b mut T: AddAssign<&'b T>,

...你会写:

where for <'b> T: AddAssign<&'b T>,

(到目前为止没有其他行改变)


但是,您注意到代码 still won't compile:

error[E0277]: the trait bound `for<'b> {integer}: std::ops::AddAssign<&'b {integer}>` is not satisfied
  --> src/main.rs:13:38
   |
13 |     println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
   |                                      ^^^^^^^^^^^^^^^ no implementation for `{integer} += &'b {integer}`
   |
   = help: the trait `for<'b> std::ops::AddAssign<&'b {integer}>` is not implemented for `{integer}`
   = note: required by `add_assign_test`

原因很简单:对于将不可变引用作为 rhs (Docs) 的原始类型,根本没有 AddAssign 的实现。我不知道这是否是一个疏忽——可能值得在 Rust 存储库上提出一个问题。

为了验证上面的代码是否有效,我编写了自己的类型并适当地实现了 AddAssignPlayground.

根据 Lukas 的回复更正了代码片段:

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

fn add_test<'a, T>(x: &'a T, y: &'a T) -> T
where
    for<'b> &'b T: Add<Output = T>,
{
    x + y
}

fn add_assign_test<'a, T>(x: &'a mut T, y: &'a T) -> T
where
    for<'b> T: AddAssign<&'b T>,
    T: Clone,
{
    *x += y;
    x.clone()
}

fn main() {
    println!("add_test()={}", add_test(&1, &2));
    println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
}

看来这是 Rust 本身的问题。当前有一个跟踪器 for this issue

在解决此问题之前,有两种可能的解决方法:

  1. 为每个基元创建命名元组并为这些类型实现 OpAssign。但是,这确实会迫使您 'cast' 所有基元到您的自定义类型。

  2. 复制 'generic' 具有原语特化的代码。