如何实现涉及 trait 对象的内置类型的通用可交换 std::ops?

trb*_*abb 8 generics operator-overloading traits rust

我有:

use std::ops::{Add, Div, Mul, Neg, Sub};

pub trait Hilbert:
    Add + Sub + Mul + Div + Neg + Mul<f64, Output = Self> + Div<f64, Output = Self> + Sized + Copy
{
    fn dot(&self, other: &Self) -> f64;
    fn magnitude(&self) -> f64;
}

fn g<T: Hilbert>(x: T) -> f64 {
    let a = (x * 2.0).dot(&x);
    let b = (2.0 * x).dot(&x);
    a + b
}
Run Code Online (Sandbox Code Playgroud)
use std::ops::{Add, Div, Mul, Neg, Sub};

pub trait Hilbert:
    Add + Sub + Mul + Div + Neg + Mul<f64, Output = Self> + Div<f64, Output = Self> + Sized + Copy
{
    fn dot(&self, other: &Self) -> f64;
    fn magnitude(&self) -> f64;
}

fn g<T: Hilbert>(x: T) -> f64 {
    let a = (x * 2.0).dot(&x);
    let b = (2.0 * x).dot(&x);
    a + b
}
Run Code Online (Sandbox Code Playgroud)

我想对所有sH * a平等。在另一个答案的脉络中,我会尝试:a * HHilbertH

impl<T: Hilbert> Mul<T> for f64 {
    type Output = T;

    fn mul(self, other: T) -> T {
        other * self
    }
}
Run Code Online (Sandbox Code Playgroud)

但这会产生:

error[E0277]: cannot multiply `T` to `{float}`
  --> src/main.rs:12:18
   |
12 |     let b = (2.0 * x).dot(&x);
   |                  ^ no implementation for `{float} * T`
   |
   = help: the trait `std::ops::Mul<T>` is not implemented for `{float}`
Run Code Online (Sandbox Code Playgroud)

为什么这是不允许的?为特征对象指定交换乘法的正确方法是什么?

Pet*_*all 6

为什么这是不允许的?

Rust 强制执行一个策略,即必须在与 trait 或类型相同的 crate 中定义实现。既不在Mul也不f64在您的板条箱中。

这可以防止关于将使用哪个实现的歧义。这使得编译器很容易强制每个类型最多存在一个特征的实例,因为它只需要检查这些 crate 中的实现。如果任何其他 crate 可以定义实例,那么编译器将不得不到处寻找。但是,试图对代码进行推理的人也必须熟悉每个 crate,以便猜测最终会使用哪种实现。Trait 实现在 Rust 中不是命名项,因此您甚至无法明确说明它。这是一些背景

一种常见的解决方法是使用包装器类型。这样做的运行时成本为零,但它会使 API 变得有点麻烦。

您也可以定义自己的数字特征,刚刚意味着所有的AddMul等等,实现的是对所有的基本类型,并用它作为约束Hilbert,而不是所有的个人特质。

但是无论你走哪条路,这都会很混乱。我会质疑对标量、非标量和混合使用相同运算符的好处。这将是迄今为止简单只需添加一个新的方法,以您的API:

fn scale(self, by: f64) -> Self;
Run Code Online (Sandbox Code Playgroud)

除了不会陷入所有这些特征边界和变通方法的复杂混乱之外,代码的意图更加清晰。您无需查看每个变量的类型即可将其与两个标量的乘法区分开来。

fn g<T: Hilbert>(x: T) -> f64 {
    let a = x.scale(2.0).dot(&x);
    let b = x.scale(2.0).dot(&x);
    a + b
}
Run Code Online (Sandbox Code Playgroud)

  • 如果确实相同,您可以在特征中提供默认实现。 (2认同)