generics - 如何在特征边界中指定 `std::ops::Mul` 的预期结果?

标签 generics rust operator-overloading traits

我有:

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 不能保证类型 T ,具有特征 Hilbert , 实现了 std::ops::Mul谁的::Output类型等于 T (Hilbert)。

但我知道(和/或希望要求)所有人都是这种情况Hilbert s,因此功能类似于 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必须持有?

最佳答案

How do I persuade Rust that Hilbert * f64 -> Hilbert must hold?

你添加了一个 trait bound <T as Mul<f64>>::Output: Hilbert .但是,这样做会揭示您设计中的更多问题:

  1. Hilbert.dot()将第二个参数作为引用,而不是按值。但将相关行更改为 (x * 2.0).dot(&x)导致另一个错误:“预期关联类型,找到类型参数”
  2. 这是因为你定义了dot采取Self , 但可能有不同的实现 Hilbert你想繁殖。 dot需要通用:fn dot<H: Hilbert>(&self, other: &H) -> f64;
  3. 最后,借用检查器命中:(x * 2.0).dot(&x)不会让你使用 x两次,因为 mul按值获取其参数。您要么必须添加一个边界 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 的不同实现不需要交互,代码可以稍微简单一点(在 trait bounds 方面):

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)
}

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

关于generics - 如何在特征边界中指定 `std::ops::Mul` 的预期结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49973888/

相关文章:

c# - 我怎么知道一个对象是否派生自特定的泛型类?

java - eclipse 警告 : unchecked conversion vs redundant specification of type arguments

pointers - Rust Global.dealloc vs ptr::drop_in_place vs ManuallyDrop

rust - 如何使用Clap指定 bool 命令行标志?

c++ - 如何在 C++ 中将 operator= 与匿名对象一起使用?

swift - 这个函数参数是做什么用的?

c# - 如何在运行时才知道类型时创建 Expression.Lambda?

rust - 在模块中派生可编码特征不起作用

c++ - 重载运算符中没有隐式转换

c++ - 为什么不使用只读 [] 运算符?