【问题标题】:Can a function take both IntoIterator<T> and IntoIterator<&T>?一个函数可以同时接受 IntoIterator<T> 和 IntoIterator<&T> 吗?
【发布时间】:2021-06-15 11:22:38
【问题描述】:

我想要一个带有两个参数的函数,这两个参数都可以变成Foo 的迭代器。问题是我想接受IntoIterator&lt;Foo&gt;IntoIterator&lt;&amp;Foo&gt; 的东西。重要的是FooCopy,所以我可以从它的引用中廉价地创建一个拥有的副本。

我目前的解决方案是:

use std::borrow::Cow;
use std::iter::IntoIterator;

fn main() {
    let foos = [Foo {}, Foo {}, Foo {}];
    let references = || foos.iter();
    let owned = || foos.iter().cloned();
    bar(references(), references());
    bar(references(), owned());
    bar(owned(), references());
    bar(owned(), owned());
}

#[derive(Copy, Clone)]
struct Foo {
    // code ommitted here
}

impl<'a> From<Foo> for Cow<'a, Foo> {
    fn from(foo: Foo) -> Self {
        Self::Owned(foo)
    }
}

impl<'a> From<&'a Foo> for Cow<'a, Foo> {
    fn from(foo: &'a Foo) -> Self {
        Self::Borrowed(foo)
    }
}

fn bar<'a, AIter, A, BIter, B>(alpha_iter: AIter, beta_iter: BIter)
  where 
    AIter: IntoIterator<Item=A>,
    A: Into<Cow<'a, Foo>>,
    BIter: IntoIterator<Item=B>,
    B: Into<Cow<'a, Foo>>
{
    for (alpha, beta) in alpha_iter.into_iter().zip(beta_iter.into_iter()) {
       some_foo_specific_thing(*alpha.into(), *beta.into());
    }
}

fn some_foo_specific_thing(alpha: Foo, beta: Foo) {
    // code ommitted here
}

playground

有没有办法在更少的行/没有Cow 的情况下做到这一点?

【问题讨论】:

    标签: rust


    【解决方案1】:

    首先,您不需要在这里完全绑定IntoIteratorIterator&lt;Item = Foo&gt; 就够了。

    use std::borrow::Cow;
    use std::iter::IntoIterator;
    
    fn main() {
        let foos = [Foo {}, Foo {}, Foo {}];
        let references = || foos.iter(); // it is iterator itself
        let owned = || foos.iter().cloned(); // it is iterator itself
        bar(references(), references());
        bar(references(), owned());
        bar(owned(), references());
        bar(owned(), owned());
    }
    
    fn bar<'a, AIter, A, BIter, B>(alpha_iter: AIter, beta_iter: BIter)
      where 
        AIter: Iterator<Item = A>,
        A: Into<Cow<'a, Foo>>,
        BIter: Iterator<Item = B>,
        B: Into<Cow<'a, Foo>>
    {
        for (alpha, beta) in alpha_iter.zip(beta_iter) {
           some_foo_specific_thing(*alpha.into(), *beta.into());
        }
    }
    

    其次,我认为最好使用Borrow trait:

    fn bar<'a, AIter, A, BIter, B>(alpha_iter: AIter, beta_iter: BIter)
      where 
        AIter: Iterator<Item = A>,
        A: std::borrow::Borrow<Foo> + Copy,
        BIter: Iterator<Item = B>,
        B: std::borrow::Borrow<Foo> + Copy,
    {
        let alpha_iter = alpha_iter.map(|item| *item.borrow()); // here we  get a reference to an item and then make a copy to own it
        let beta_iter = beta_iter.map(|item| *item.borrow()); // here we  get a reference to an item and then make a copy to own it
        
        for (alpha, beta) in alpha_iter.zip(beta_iter) {
           some_foo_specific_thing(alpha, beta);
        }
    }
    

    【讨论】:

    • 在绝大多数情况下,没有理由推荐Iterator 而不是IntoIteratorIntoIterator 是更通用、更可取的绑定,因为它会自动为所有迭代器以及可迭代对象实现。而且,使用Into的版本无法编译。
    • 对于一个没有涵盖它的最小测试用例我也不好,但我将它用于其他不是迭代器的东西(例如切片引用)
    • 然而:宾果游戏!我知道我缺少一个特质,Borrow 就是答案!非常感谢@Dmitry :D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    相关资源
    最近更新 更多