如何获得 Rust 嵌套泛型

How to get a rust nested generic

我正在尝试编写一个通用结构来保存一个集合以及该集合中所有插入值的总和。

这是一个泛型类型,它保留所有附加值的总和。

pub struct SummedCollection<T>
where 
    T::Item: std::ops::Add<Output=T::Item> + std::ops::Div<Output=T::Item>
{
    sum: T::Item,
    values: T,
}

impl<T> SummedCollection<T> { 
    pub fn new() -> Self{
        SummedCollection {
            sum: T::Item::default(),
            values: T::new(),
        }
    }
    pub fn push(&mut self, value: T::Item) {
        self.values.push(value);
        self.sum = self.sum + value;
    }
    pub fn sum(&self) -> T::Item {
        self.sum
    }
}

预期用途是:

let v: SummedCollection<Vec<i32>> = SummedCollection::new();
v.push(5);
v.push(10);

然后我会期望:v.sum() == 15

我在 T::Item 的每个保证上都收到错误消息“^^^^ 未找到关联类型 `Item`”, 我需要做什么才能访问嵌套泛型(我的示例中的 i32)?

编译器不知道T::Item。您希望 T 成为某种集合类型,但没有告诉编译器,所以它不知道。

你必须告诉编译器 T 实现一些集合特性。标准库中不存在这样的特征,但您可以轻松编写自己的特征:

pub trait Collection {
    type Item;
    fn push(&mut self, value: Self::Item);
}

// Example implementation, you can implement the trait for any collection type you wish
impl<T> Collection for Vec<T> {
    type Item = T;
    fn push(&mut self, value: T) {
        self.push(value);
    }
}

pub struct SummedCollection<T: Collection> {
    sum: T::Item,
    values: T,
}

impl<T> SummedCollection<T>
where
    T: Collection + Default,
    T::Item: Default + Copy + std::ops::Add<Output = T::Item> + std::ops::Div<Output = T::Item>,
{
    pub fn new() -> Self {
        SummedCollection {
            sum: T::Item::default(),
            values: T::default(),
        }
    }
    pub fn add(&mut self, value: T::Item) {
        self.values.push(value);
        self.sum = self.sum + value;
    }
    pub fn sum(&self) -> T::Item {
        self.sum
    }
}

请注意,我做了一些额外的更改:

  • 要求 T::ItemCopyDefault。或许可以解决此需求,但这样做很容易。
  • 需要 T: Default 并将 T::new() 更改为 T::default(),因为我们已经具有 default-constructible 特征 - 无需重新发明轮子。
  • 将一些边界从结构移到了实现,因为

编辑: 感谢@mcarton 指出我们可以使用 Extend

#[derive(Debug)]
pub struct SummedCollection<Collection, T> {
    sum: T,
    values: Collection,
}

impl<Collection, T> SummedCollection<Collection, T>
where
    Collection: Extend<T> + Default,
    T: Default + Copy + std::ops::Add<Output = T> + std::ops::Div<Output = T>,
{
    pub fn new() -> Self {
        SummedCollection {
            sum: T::default(),
            values: Collection::default(),
        }
    }
    pub fn add(&mut self, value: T) {
        self.values.extend(std::iter::once(value));
        self.sum = self.sum + value;
    }
    pub fn sum(&self) -> T {
        self.sum
    }
}

但请注意,因为它需要一个额外的通用参数,所以它会影响用户:而不是

let v: SummedCollection<Vec<i32>> = SummedCollection::new();

你必须写

let v: SummedCollection<Vec<i32>, _> = SummedCollection::new();

或者明确地说,当然:

let v: SummedCollection<Vec<i32>, i32> = SummedCollection::new();