【问题标题】:How can I satisfy the `Sum<T>` trait requirement for f32?如何满足 f32 的 `Sum<T>` 特征要求?
【发布时间】:2022-01-19 19:20:54
【问题描述】:

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

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

我的问题与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>

但即使我尝试包含f32: Sum&lt;&amp;T&gt;,我仍然会遇到同样的错误。 我在这里做错了什么,我该如何解决这个问题?提前致谢,如果您需要任何进一步的说明,请告诉我。

【问题讨论】:

    标签: generics rust iterator traits


    【解决方案1】:

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

    让我们开始吧。

    1。复制数字

    看看implementors for 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
    ...
    

    等等。

    一般模式很明显:

    impl Sum<T> for T
    

    它们都不匹配您的代码,因为它显然试图使用Sum&lt;&amp;T&gt;。我们需要去掉引用,所以Sum&lt;&amp;T&gt; 可以是Sum&lt;T&gt;

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

    我们可以使用into_iter() 直接迭代项目。不过,我们不要。 into_iter() 将集合转换为迭代器。转换……就像破坏一样。它移动所有项目并在此过程中使用该集合。哎呀!

    相反,让我们复制带有copied() 的项目:

    当您有一个基于&amp;T 的迭代器时,这很有用,但您需要一个基于T 的迭代器。

    let sum: f32 = self.iter().copied().sum();
    

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

    impl<T> CentralMoment for [T]
    where
        T: Copy,
    

    这会将错误更改为:

    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`
    

    Playground

    2。求和的结果是T

    当你总结一堆i32s 时,你会得到一个i32。当您将一堆f64s 相加时,您将得到一个f64。这段代码表明sum() 的结果将是f32,但要真正通用,您应该将其称为T

    let sum: T = self.iter().copied().sum();
    

    现在错误是:

    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`
    

    3。 T 必须是可累加的

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

    where
        T: Copy + Sum,
    

    现在我们得到:

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

    Playground

    4。 T 必须能被 f32 整除

    我们很接近了,对吧?让我们要求T 可以被f32s 整除:

    where
        T: Copy + Sum + Div<f32>,
    

    你快乐吗,编译器?

    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`
    

    Playground

    不,当然不是。好的,好的,现在呢?

    5。部门必须返回f32

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

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

    where
        T: Copy + Sum + Div<f32, Output = f32>,
    

    结果:

    Compiling playground v0.0.1 (/playground)
     Finished dev [unoptimized + debuginfo] target(s) in 0.91s
    

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

    Playground

    解决方案

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

    Playground

    【讨论】:

    • 谢谢!这不仅是我正在寻找的解决方案,而且感谢您的简洁解释。打地鼠正是我正在做的。我经常发现自己在使用 Rust 的情况下(尤其是泛型)。
    • 请注意,唯一的标准库类型 Div&lt;f32, Output = f32&gt; 是 `f32,因此除非您为自定义类型实现该特征,否则 impl 实际上不会是通用的。
    • 太棒了答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-04
    • 2022-11-27
    • 2021-11-29
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多