我可以使用在函数内部创建的值扩展迭代器吗?

时间:2019-03-30 05:26:21

标签: rust iterator

我有一个program对其参数执行scan操作:

struct A(usize);
struct B(usize);

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let accum = 0;

    xs.iter().scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

我想用在函数内部生成的一些值扩展迭代器:

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let accum = 0;
    let head: A = A(xs.len());

    use std::iter::once;
    once(head).chain(xs.iter()).scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

这不会编译,因为once(head)A的迭代器,而xs.iter()&A的迭代器。

我可以为Clone实现A并将.cloned()放在xs.iter()之后进行修复,但是我不想克隆整个xs,因为可能会很长,并且实际程序中的A克隆起来并不便宜。

我正在寻找一种将once(head)转换为&A的迭代器的方法,但是找不到任何方法。

能否使其正常工作?

2 个答案:

答案 0 :(得分:2)

  

我可以使用在函数内部创建的值扩展迭代器吗?

是:

fn example<'a>(input: impl Iterator<Item = i32> + 'a) -> impl Iterator<Item = i32> + 'a {
    Some(42).into_iter().chain(input).chain(Some(99))
}

fn main() {
    for i in example(vec![1, 2, 3].into_iter()) {
        println!("{}", i);
    }
}
  

我正在寻找一种将once(head)变成&A的迭代器的方法

引用该值:

iter::once(&head)
  

是否可以使[此特定代码]正常工作?

不。编译器甚至会告诉您:

error[E0515]: cannot return value referencing local variable `head`
  --> src/lib.rs:10:5
   |
10 |       iter::once(&head).chain(xs.iter()).scan(accum, |accum, x| {
   |       ^          ----- `head` is borrowed here
   |  _____|
   | |
11 | |         *accum += x.0;
12 | |         Some(B(*accum))
13 | |     })
   | |______^ returns a value referencing data owned by the current function

另请参阅:

  

是否有可能使[接近此代码的内容]正常工作?

也许。由于scan以累加器值开头,因此您可以使用它而不是将其粘贴到迭代器上:

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    xs.iter().scan(xs.len(), |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

这意味着生成的迭代器少了一项。是否可以接受取决于您的用法。

一个更复杂的解决方案是拥有一个代表借入值或拥有值的枚举。然后,您可以根据输入本地值创建这些枚举的迭代器。本地值的所有权转移到返回的迭代器中:

struct A(usize);
struct B(usize);

use std::iter;

// `A` doesn't implement `Clone`; if it did, use `Cow`
enum OwnedOrBorrowed<'a, T> {
    Owned(T),
    Borrowed(&'a T),
}

impl<'a, T> std::ops::Deref for OwnedOrBorrowed<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        match self {
            OwnedOrBorrowed::Owned(t) => t,
            OwnedOrBorrowed::Borrowed(t) => t,
        }
    }
}

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let accum = 0;
    let head = OwnedOrBorrowed::Owned(A(xs.len()));

    let borrowed = xs.iter().map(OwnedOrBorrowed::Borrowed);

    iter::once(head).chain(borrowed).scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

这不是免费的-scan闭包的每次调用都会执行条件逻辑,以测试该值是否已拥有或借用。

另请参阅:

答案 1 :(得分:0)

Outside of the function

fn main() {
    let a: &[A] = &[A(1), A(2), A(3)];
    let b: &[A] = &[A(a.len())];
    for s in scan_something(b, a) {
        println!("{:?}", s);
    }
}

fn scan_something<'a>(xs1: &'a [A], xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let iter3 = xs1.iter().chain(xs.iter());
    let accum = 0;
    iter3.scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

#[derive(Debug)]
struct A(usize);

#[derive(Debug)]
struct B(usize);

输出:

B(3)
B(4)
B(6)
B(9)