【问题标题】:Idiomatic way to accept owned and referenced iterables?接受拥有和引用的迭代的惯用方式?
【发布时间】:2023-01-08 09:39:49
【问题描述】:

考虑这个生锈代码: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d6f2075a8872305334a8ba513241950b

fn main() {
    let v: Vec<i32> = vec![1, 2, 3];
    // This works, of course.
    println!("{}", foo(&v));
    
    // Now let's create an "extra conversion" step:
    let vs: Vec<&str> = vec!["1", "2", "3"];
    
    // We want to "stream" straight from this vec. Let's create an 
    // iterator that converts:
    let converting_iterator = vs.iter().map(|s| s.parse::<i32>().unwrap());
    
    // This does not work
    println!("{}", foo(converting_iterator));
}

fn foo<'a>(it: impl IntoIterator<Item=&'a i32>) -> i32 {
    it.into_iter().sum()
}

我明白为什么第二行不起作用。它在 i32 而不是 &amp;i32 上创建迭代器。我不能只是将 &amp; 放入闭包中,因为那样会尝试引用一个临时值。

我很好奇的是,如果有任何以可以处理两种类型的可迭代对象的方式编写foo的方法?如果我只是将 .sum() 添加到创建 converting_iterator 的末尾,它就会正常工作。所以我觉得应该有一些“拦截”结果(即转换迭代器)的方法,通过对某事,并有那东西打电话给.sum

可能与 BorrowAsRef 有关,但我无法从这些特征的文档中弄清楚。

【问题讨论】:

  • 为什么不直接将foo 更改为接受impl Iterator&lt;Item=i32&gt;
  • 因为第一行不再有效,foo(&amp;v)
  • 但也许我对什么是惯用语有误解。我假设一般来说,对于函数参数,如果你不这样做需要获得所有权,然后使用借用“更好”。
  • 由于i32Copy 类型,我可能会让调用者进行转换:foo(v.iter().copied())。您是否正在寻找也适用于非复制类型的解决方案?

标签: rust iterator traits ownership


【解决方案1】:

特别是总和,以下工作:

fn foo<Item>(it: impl IntoIterator<Item=Item>) -> i32
where i32: Sum<Item>
{
    it.into_iter().sum()
}

【讨论】:

  • 谢谢!所以我想无论我想要什么操作,我都需要适当的特征界限。
【解决方案2】:

一个更广泛的答案是使用 Borrow 特性来一般地接受引用和非引用:

use std::borrow::Borrow;

fn foo<'a, T: Borrow<i32>>(it: impl IntoIterator<Item = T>) -> i32 {
    it.into_iter().map(|t| *t.borrow()).sum()
}

不幸的是,它不如 PitaJ's answer 好,因为 .sum() 已经可以同时处理引用和非引用,但不能处理 impl Borrow,所以我们必须引入 .map() 来从潜在借用类型。但如果您的功能不同,这是一个可以使用的技巧。

这是在其他问题中看到的模式:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-16
    • 2021-11-09
    • 2017-12-02
    • 1970-01-01
    • 2021-01-30
    • 2020-08-02
    • 2016-07-21
    • 2021-03-28
    相关资源
    最近更新 更多