我有:

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

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

fn g<T: Hilbert>(x: T) -> f64 {
    return (x * 2.0).dot(x);
}

...产生:

error[E0599]: no method named `dot` found for type `<T as std::ops::Mul<f64>>::Output` in the current scope
 --> src/main.rs:9:22
  |
9 |     return (x * 2.0).dot(x);
  |                      ^^^
  |
  = help: items from traits can only be used if the trait is implemented and in scope
  = note: the following trait defines an item `dot`, perhaps you need to implement it:
          candidate #1: `Hilbert`

我将其解释为表示Rust无法保证类型为THilbert类型具有std::ops::Mul的实现,其::Output类型等于T(一个Hilbert)。

但是我知道(和/或希望要求)所有Hilbert都是这种情况,因此可以编写类似g()的函数。

我想将std::ops::Mul::Output表示为Hilbert:
impl<T: Hilbert> Mul<f64> for T {
    type Output = T;
}

...但是这同时存在一些问题,即(a)我无法“部分实现”一个特征,并且将被迫为所有Mul::mul()生成函数Hilberts的泛型实现,但是Mul::mul()的实际实现将取决于Hilbert的具体实现; (b)似乎根本不允许我写这个特征:

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`); only traits defined in the current crate can be implemented for a type parameter
  --> src/main.rs:12:1
   |
12 | / impl<T: Hilbert> Mul<f64> for T {
13 | |     type Output = T;
14 | | }
   | |_^

我如何说服Rust保留Hilbert * f64-> Hilbert

最佳答案



您添加一个特征绑定(bind)<T as Mul<f64>>::Output: Hilbert。但是,这样做将揭示设计中的其他问题:

  • Hilbert.dot()将第二个参数作为引用,而不是按值。但是将相关行更改为(x * 2.0).dot(&x)会导致另一个错误:“预期的关联类型,找到的类型参数”。
  • 这是因为您将dot定义为采用Self,但是要乘以Hilbert可能有不同的实现。 dot必须是通用的:fn dot<H: Hilbert>(&self, other: &H) -> f64;
  • 最后,借用检查器会命中:(x * 2.0).dot(&x)不会让您两次使用x,因为mul会按值接受参数。您要么必须添加绑定(bind)的Mul<&'a Self>才能传递引用(这会使用生命周期参数感染您的API),要么使x可克隆(我认为不可复制将适用)。

  • 将以上所有结果应用到此可工作的可编译代码中:
    pub trait Hilbert: Add + Sub + Mul + Div + Neg + Mul<f64> + Div<f64> + Sized {
        fn dot<H: Hilbert>(&self, other: &H) -> f64;
        fn magnitude(&self) -> f64;
    }
    
    fn g<T: Hilbert + Clone>(x: T) -> f64
    where
        <T as Mul<f64>>::Output: Hilbert,
    {
        (x.clone() * 2.0).dot(&x)
    }
    

    如果Hilbert.dot不应该是通用的,因为Hilbert的不同实现不需要交互,则代码可以稍微简单一些(就特征范围而言):
    pub trait Hilbert:
        Add + Sub + Mul + Div + Neg + Mul<f64, Output = Self> + Div<f64, Output = Self> + Sized
    {
        fn dot(&self, other: &Self) -> f64;
        fn magnitude(&self) -> f64;
    }
    
    fn g<T: Hilbert + Clone>(x: T) -> f64 {
        (x.clone() * 2.0).dot(&x)
    }
    

    但是,根据我对希尔伯特变换的了解,后一种情况似乎不太有用。

    09-08 02:56