我有一个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
的迭代器,但找不到任何方法。
有可能让它发挥作用吗?
我可以使用在函数内创建的值扩展迭代器吗?
是:
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
的闭包都会执行条件逻辑来测试值是拥有还是借用。
也可以看看:
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)