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

问题描述 投票:1回答:2

我有一个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和put.cloned()实现xs.iter()来解决这个问题,但我不想克隆整个xs,因为它可能很长并且实际程序中的A克隆并不便宜。

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

有可能让它发挥作用吗?

rust iterator
2个回答
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的闭包都会执行条件逻辑来测试值是拥有还是借用。

也可以看看:


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)
© www.soinside.com 2019 - 2024. All rights reserved.