【问题标题】:How to implement the extension trait pattern for functions that return `impl Trait`?如何为返回`impl Trait`的函数实现扩展特征模式?
【发布时间】:2021-06-01 00:39:13
【问题描述】:

假设我有以下辅助方法,它使用向量加法将两个迭代器(或IntoIterators)相加:

fn add<I1, I2, T>(a: I1, b: I2) -> impl Iterator<Item = T>
where
    I1: IntoIterator<Item = T>,
    I2: IntoIterator<Item = T>,
    T: std::ops::Add<Output = T>,
{
    a.into_iter().zip(b).map(|(x, y)| x + y)
}

可以这样使用:

fn main() {
    let a = vec![1, 2, 3, 4, 5]; // Can use vectors
    let b = (6..=10).map(|x| x + 1); // Or other complicated iterators
    let sum_iter = add(a, b); // Can be used as an iterator if you wish to chain other functions
    let sum_vec = sum_iter.collect::<Vec<i32>>(); // Or you can collect it
    assert_eq!(sum_vec, vec![8, 10, 12, 14, 16]);
}

在实现这个之后,我决定我应该更改 API,以便我可以使用语法 a.add(b),这将使链接这些函数非常好。我试图实现extension trait 模式,但很快意识到这是不可能的,因为特征函数的返回类型不支持impl Trait,并且我的add 函数由于闭包@ 而无法指定具体类型987654328@ 即传入.map()

为了帮助澄清我的想法,这基本上是我尝试过的(显然这不会编译):

trait IntoIteratorMathExt
where
    Self: IntoIterator,
{
    fn add<I>(self, other: I) -> impl Iterator<Item = Self::Item> 
    where
        I: IntoIterator<Item = Self::Item>,
        Self::Item: std::ops::Add<Output = Self::Item>;
}

impl<T: IntoIterator> IteratorMathExt for T {
    fn add<I>(self, other: I) -> impl Iterator<Item = Self::Item>
    where
        I: IntoIterator<Item = Self::Item>,
        Self::Item: std::ops::Add<Output = Self::Item>,
    {
        self.into_iter().zip(other).map(|(x, y)| x + y)
    }
}

fn main() {
    let a = vec![1, 2, 3, 4, 5];
    let b = (6..=10).map(|x| x + 1);
    let sum_iter = a.add(b); // <- Really nice syntax!
    let sum_vec = sum_iter.collect::<Vec<i32>>();
    assert_eq!(sum_vec, vec![8, 10, 12, 14, 16]);
}

不出所料,这会产生以下编译器错误:

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> src\main.rs:34:34
   |
34 |     fn add<I>(self, other: I) -> impl Iterator<Item = Self::Item>
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> src\main.rs:41:34
   |
41 |     fn add<I>(self, other: I) -> impl Iterator<Item = Self::Item>
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我理解为什么 trait 方法不支持 impl Trait,但是,我的 trait “实际上”只在一个地方实现过,所以我希望可能存在一种解决方法。

所以放弃这种方法,我想知道实现这种模式的“正确”方法是什么。

目标是能够使用迭代器 abIteratorIntoIterator,我不是 100% 什么是最好的),然后能够编写 a.add(b) 和让它返回一个Iterator,与ab 具有相同的关联Item 类型。

有什么建议吗?

【问题讨论】:

    标签: rust iterator traits


    【解决方案1】:

    由于你的闭包不捕获任何值,它实际上可以表示为函数指针类型,它允许你表示类型(它很丑,但它有效):

    type MathExtType<A, B, T> = std::iter::Map<std::iter::Zip<A, B>, fn((T, T)) -> T>;
    
    trait IntoIteratorMathExt
    where
        Self: IntoIterator,
    {
        fn add<I>(self, other: I) -> MathExtType<Self::IntoIter, I::IntoIter, Self::Item>
        where
            I: IntoIterator<Item = Self::Item>,
            Self::Item: std::ops::Add<Output = Self::Item>;
    }
    
    impl<T: IntoIterator> IntoIteratorMathExt for T {
        fn add<I>(self, other: I) -> MathExtType<Self::IntoIter, I::IntoIter, Self::Item>
        where
            I: IntoIterator<Item = Self::Item>,
            Self::Item: std::ops::Add<Output = Self::Item>,
        {
            self.into_iter().zip(other).map(|(x, y)| x + y)
        }
    }
    
    fn main() {
        let a = vec![1, 2, 3, 4, 5];
        let b = (6..=10).map(|x| x + 1);
        let sum_iter = a.add(b); // <- Really nice syntax!
        let sum_vec = sum_iter.collect::<Vec<i32>>();
        assert_eq!(sum_vec, vec![8, 10, 12, 14, 16]);
    }
    

    【讨论】:

    • 是的。我实际上考虑过这一点,并且可能在以最小的方式表达我的问题方面做得不好。例如,我可以将 add 方法更改为接受一个常量(即像 a.add(50) 一样使用),在这种情况下,闭包看起来像 |x| x + constant。在这种情况下,我将无法使用函数指针,因为闭包必须捕获常量。
    【解决方案2】:

    如果您可以按照动态方式走下去,事情就会变得更容易。你可以返回一个Box&lt;dyn Iterator&lt;Item=Self::Item&gt;&gt;,一切都会好起来的。

    【讨论】:

      猜你喜欢
      • 2021-02-01
      • 2017-01-21
      • 1970-01-01
      • 2020-03-24
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 1970-01-01
      相关资源
      最近更新 更多