如何满足 f32 的 `Sum<T>` 特征要求?

Cza*_*ero 3 generics iterator traits rust

我有以下我正在尝试实现的特征:

pub trait CentralMoment<Output = f32>
where
    Output: Copy,
{
    fn mean(&self) -> Output;
}

impl<T> CentralMoment for [T] {
    fn mean(&self) -> f32 {
        let sum: f32 = self.iter().sum();
        sum / self.len() as f32
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是线路let sum: f32 = self.iter().sum()。编译器告诉我:

the trait bound `f32: Sum<&T>` is not satisfied
  --> src/lib.rs:45:36
   |
45 |         let sum: f32 = self.iter().sum();
   |                                    ^^^ the trait `Sum<&T>` is not implemented for `f32`
   |
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
   |
42 |     T: Copy, f32: Sum<&T>
Run Code Online (Sandbox Code Playgroud)

但即使当我尝试包含时,f32: Sum<&T>我仍然遇到相同的错误。我在这里做错了什么以及如何解决这个问题?提前致谢,如果您需要任何进一步说明,请告诉我。

Joh*_*ica 10

泛型的算术很棘手。正确设置所有约束和类型就像打地鼠游戏。每次你修复一个错误时,另一个错误就会弹出。我不想直接跳到答案;如果我们逐一完成每个步骤,效果会更好。也就是说,如果您只想查看解决方案,它位于底部。

让我们开始敲击吧。

1. 复制数字

看一下 的实现者Sum。它们看起来像这样:

impl Sum<f32> for f32
impl Sum<f64> for f64
impl Sum<i8> for i8
impl Sum<i16> for i16
impl Sum<i32> for i32
...
Run Code Online (Sandbox Code Playgroud)

等等。

一般模式很明显:

impl Sum<T> for T
Run Code Online (Sandbox Code Playgroud)

它们都不符合您的代码,因为它显然正在尝试使用Sum<&T>. 我们需要删除引用,这样就Sum<&T>可以了Sum<T>

参考资料从哪里来?iter()迭代项目引用。这样做是为了避免修改集合或其项目。

我们可以用来into_iter()直接迭代项目。不过,我们还是不要这样吧。into_iter()将集合转换迭代器。转换...就像破坏一样。它会移动所有项目并在此过程中消耗集合。哎呀!

相反,让我们使用以下命令复制项目copied()

&T当您有一个迭代器 over ,但您需要一个迭代器 over时,这很有用T

let sum: f32 = self.iter().copied().sum();
Run Code Online (Sandbox Code Playgroud)

我们还需要一个T: Copy约束:

impl<T> CentralMoment for [T]
where
    T: Copy,
Run Code Online (Sandbox Code Playgroud)

这会将错误更改为:

error[E0277]: the trait bound `f32: Sum<T>` is not satisfied
  --> src/lib.rs:13:45
   |
13 |         let sum: f32 = self.iter().copied().sum();
   |                                             ^^^ the trait `Sum<T>` is not implemented for `f32`
Run Code Online (Sandbox Code Playgroud)

操场

2. 求和的结果为T

当你总结一堆i32s 时,你会得到一个i32。当你对一堆f64s 求和时,你会得到一个f64. 这段代码表示 的结果sum()将是 an f32,但实际上为了正确通用,您应该将其称为 a T

let sum: T = self.iter().copied().sum();
Run Code Online (Sandbox Code Playgroud)

现在错误是:

error[E0277]: the trait bound `T: Sum` is not satisfied
  --> src/lib.rs:13:43
   |
13 |         let sum: T = self.iter().copied().sum();
   |                                           ^^^ the trait `Sum` is not implemented for `T`
Run Code Online (Sandbox Code Playgroud)

3.T必须是可求和的

我们可以解决这个问题,要求T: Sum

where
    T: Copy + Sum,
Run Code Online (Sandbox Code Playgroud)

现在我们得到:

error[E0369]: cannot divide `T` by `f32`
  --> src/lib.rs:16:13
   |
16 |         sum / self.len() as f32
   |         --- ^ ----------------- f32
   |         |
   |         T
Run Code Online (Sandbox Code Playgroud)

操场

4.T必须能被整除f32

我们很接近,对吧?我们要求它能T被 s 整除f32

where
    T: Copy + Sum + Div<f32>,
Run Code Online (Sandbox Code Playgroud)

编译器,你高兴吗?

error[E0308]: mismatched types
  --> src/lib.rs:17:9
   |
15 |     fn mean(&self) -> f32 {
   |                       --- expected `f32` because of return type
16 |         let sum: T = self.iter().copied().sum();
17 |         sum / self.len() as f32
   |         ^^^^^^^^^^^^^^^^^^^^^^^ expected `f32`, found associated type
   |
   = note:         expected type `f32`
           found associated type `<T as Div<f32>>::Output`
   = help: consider constraining the associated type `<T as Div<f32>>::Output` to `f32`
Run Code Online (Sandbox Code Playgroud)

操场

不,当然不。好吧好吧,现在怎么办?

5. 部门必须返回一份f32

好吧,我们要求它能T被 整除f32。不过,我们还没有告诉它结果是什么类型。操作数类型和结果类型并不一定相同。他们可能会有所不同。Rust 非常灵活。

::Output我们需要对操作施加约束。我们要求输出也是f32

where
    T: Copy + Sum + Div<f32, Output = f32>,
Run Code Online (Sandbox Code Playgroud)

结果:

Compiling playground v0.0.1 (/playground)
 Finished dev [unoptimized + debuginfo] target(s) in 0.91s
Run Code Online (Sandbox Code Playgroud)

哈利路亚!公主不在另一座城堡里。

操场

解决方案

use std::iter::Sum;
use std::ops::Div;

pub trait CentralMoment<Output = f32>
where
    Output: Copy,
{
    fn mean(&self) -> Output;
}

impl<T> CentralMoment for [T]
where
    T: Copy + Sum + Div<f32, Output = f32>,
{
    fn mean(&self) -> f32 {
        let sum: T = self.iter().copied().sum();
        sum / self.len() as f32
    }
}
Run Code Online (Sandbox Code Playgroud)

操场

  • 请注意,唯一的标准库类型是 `Div&lt;f32, Output = f32&gt;` 是 `f32,因此除非您为自定义类型实现该特征,否则 impl 实际上不会是通用的。 (5认同)