首页 > 解决方案 > 为迭代器实现另一个特征

问题描述

我正在尝试为所有Iterators 添加另一个特征。但我不明白为什么它不能编译。

这是代码:

use std::fmt::Display;
use std::iter::Sum;

trait Topla<T> {
    fn topla(&mut self)-> T;
}

impl<T, I> Topla<T> for I
where
    T: Sum + Display,
    I: Iterator<Item = T>,
{
    fn topla(&mut self) -> T {
        self.sum()
    }
}

fn main() {
    let data = vec![1,2,3,5,8];
    println!("{:?}", data.iter().topla());
}

标签: rustiteratortraits

解决方案


如果我们使用完全限定的 trait 语法,问题就会显现出来:

fn main() {
    let data = vec![1,2,3,5,8u32];
    let mut iter = data.iter();
    println!("{:?}", Topla::<u32>::topla(&mut iter));
}
error[E0271]: type mismatch resolving `<std::slice::Iter<'_, u32> as std::iter::Iterator>::Item == u32`
  --> src/main.rs:22:22
   |
5  |     fn topla(&mut self) -> T;
   |     ------------------------- required by `Topla::topla`
...
22 |     println!("{:?}", Topla::<u32>::topla(&mut iter));
   |                      ^^^^^^^^^^^^^^^^^^^ expected reference, found `u32`
   |
   = note: expected reference `&u32`
                   found type `u32`
   = note: required because of the requirements on the impl of `Topla<u32>` for `std::slice::Iter<'_, u32>`

更改Topla<u32>Topla<&u32>让我们更接近:

Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `&u32: std::iter::Sum` is not satisfied
  --> src/main.rs:22:22
   |
5  |     fn topla(&mut self) -> T;
   |     ------------------------- required by `Topla::topla`
...
22 |     println!("{:?}", Topla::<&u32>::topla(&mut iter));
   |                      ^^^^^^^^^^^^^^^^^^^^ the trait `std::iter::Sum` is not implemented for `&u32`
   |
   = help: the following implementations were found:
             <u32 as std::iter::Sum<&'a u32>>
             <u32 as std::iter::Sum>
   = note: required because of the requirements on the impl of `Topla<&u32>` for `std::slice::Iter<'_, u32>`

问题是Sum<&u32>没有实现&u32; 它是为u32. 由于我们需要返回 au32而不是&u32,我们需要放宽对 的要求T,从作为迭代器类型本身到仅仅Sum能够使用 T。

trait Topla<T> {
    fn topla(&mut self) -> T;
}

impl<T, I> Topla<T> for I
where
    T: Display + Sum<<I as Iterator>::Item>,
    I: Iterator,
{
    fn topla(&mut self) -> T {
        self.sum()
    }
}

fn main() {
    let data = vec![1,2,3,5,8u32];
    let mut iter = data.iter();
    println!("{:?}", Topla::<u32>::topla(&mut iter));
}

但是现在如果我们回到原来的语法,我们会遇到类型推断失败,这使得我们的新 API 使用起来非常烦人。我们可以通过对 API 更严格一点来解决这个问题。

如果我们制作Topla的子特征Iterator,我们可以在方法的定义中引用项目类型topla,因此将输出的类型参数移动到方法而不是特征中。这将让我们像使用sum(). 最后我们有:

use std::fmt::Display;
use std::iter::Sum;

trait Topla: Iterator {
    fn topla<T>(self) -> T
    where
        Self: Sized,
        T: Sum<<Self as Iterator>::Item>;
}

impl<I> Topla for I
where
    I: Iterator,
    <I as Iterator>::Item: Display,
{
    fn topla<T>(self) -> T
    where
        Self: Sized,
        T: Sum<<Self as Iterator>::Item>,
    {
        self.sum()
    }
}

fn main() {
    let data = vec![1, 2, 3, 5, 8];
    println!("{:?}", data.iter().topla::<u32>());
}

推荐阅读