无法将过滤后的 Vec 收集到自身中
Unable to collect a filtered a Vec into itself
我已经开始研究 Rust 和 came across #3 where the easiest quick-approach would be to implement a Sieve of Eratosthenes 中的 Project Euler 问题。
在这样做时,我的算法创建了一个迭代器,然后从中过滤出非素数并将其分配回原始向量,但我收到一个错误,指出 Vec<u32>
无法从中构建Iterator<Item=&u32>
.
代码:
fn eratosthenes_sieve(limit: u32) -> Vec<u32> {
let mut primes: Vec<u32> = Vec::new();
let mut range: Vec<u32> = (2..=limit).collect();
let mut length = range.len();
loop {
let p = range[0];
primes.push(p);
range = range.iter().filter(|&n| *n % p != 0).collect();
if length == range.len() {
break;
}
length = range.len();
}
primes
}
错误:
error[E0277]: a collection of type `std::vec::Vec<u32>` cannot be built from an iterator over elements of type `&u32`
--> src\main.rs:42:55
|
42 | range = range.iter().filter(|&n| *n % p != 0).collect();
| ^^^^^^^ a collection of type `std::vec::Vec<u32>` cannot be built from `std::iter::Iterator<Item=&u32>`
|
= help: the trait `std::iter::FromIterator<&u32>` is not implemented for `std::vec::Vec<u32>`
为什么闭包将值包装在额外的借用中?
说明
根据错误消息,表达式 range.iter().filter(|&n| *n % p != 0)
是对 &u32
类型项的迭代器:对 u32
的引用。您期望迭代器的值超过 u32
。那么让我们向后走:
迭代器链的filter(...)
部分实际上与你的问题无关。当我们看一下Iterator::filter
, we see that it returns Filter<Self, P>
。这种类型实现 Iterator
:
impl<I: Iterator, P> Iterator for Filter<I, P>
where
P: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
// ...
}
这里重要的部分是type Item = I::Item
,这意味着I
(原始迭代器)的项类型被完全传递。没有添加参考。
剩下 .iter()
:这就是问题的原因。 Vec::iter
returns slice::Iter<T>
实现 Iterator
:
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
// ...
}
这里我们看到项目类型是对 T
(向量的元素类型)的引用。
解决方案
在一般情况下,您可以对迭代引用的任何迭代器调用 .cloned()
以获得按值迭代项目的新迭代器(通过克隆每个项目)。对于实现 Copy
的类型,您可以(并且应该)使用 .copied()
。例如。 range.iter().filter(|&n| *n % p != 0).copied().collect()
.
但是,在这种情况下有一个更好的解决方案:因为你之后不再需要向量,你可以调用 into_iter()
而不是 iter()
以便按值直接获取 u32
上的迭代器。这会消耗向量,使其之后无法访问。但是,如前所述,这在这里不是问题。
range = range.into_iter().filter(|&n| n % p != 0).collect();
另请注意,我删除了 *n
中的 *
,因为不再需要解除引用。
其他提示
总是重新分配一个新向量不是很快。 Eratosthenes 筛法通常以不同的方式实现:不是存储数字,而是只存储布尔值来表示每个数字是否为素数。这些数字永远不会显式存储,而是通过使用 vector/array 的索引隐式存储。
为了让它变得非常快,不应该使用 Vec<bool>
而应该使用专用的 bitvec。 Vec<bool>
每个 bool
存储一个字节,尽管只需要一位。提供这种位向量的事实上的箱子是 bit-vec
,它还方便地显示了埃拉托色尼筛法的示例实现 in its documentation。
我已经开始研究 Rust 和 came across #3 where the easiest quick-approach would be to implement a Sieve of Eratosthenes 中的 Project Euler 问题。
在这样做时,我的算法创建了一个迭代器,然后从中过滤出非素数并将其分配回原始向量,但我收到一个错误,指出 Vec<u32>
无法从中构建Iterator<Item=&u32>
.
代码:
fn eratosthenes_sieve(limit: u32) -> Vec<u32> {
let mut primes: Vec<u32> = Vec::new();
let mut range: Vec<u32> = (2..=limit).collect();
let mut length = range.len();
loop {
let p = range[0];
primes.push(p);
range = range.iter().filter(|&n| *n % p != 0).collect();
if length == range.len() {
break;
}
length = range.len();
}
primes
}
错误:
error[E0277]: a collection of type `std::vec::Vec<u32>` cannot be built from an iterator over elements of type `&u32`
--> src\main.rs:42:55
|
42 | range = range.iter().filter(|&n| *n % p != 0).collect();
| ^^^^^^^ a collection of type `std::vec::Vec<u32>` cannot be built from `std::iter::Iterator<Item=&u32>`
|
= help: the trait `std::iter::FromIterator<&u32>` is not implemented for `std::vec::Vec<u32>`
为什么闭包将值包装在额外的借用中?
说明
根据错误消息,表达式 range.iter().filter(|&n| *n % p != 0)
是对 &u32
类型项的迭代器:对 u32
的引用。您期望迭代器的值超过 u32
。那么让我们向后走:
迭代器链的filter(...)
部分实际上与你的问题无关。当我们看一下Iterator::filter
, we see that it returns Filter<Self, P>
。这种类型实现 Iterator
:
impl<I: Iterator, P> Iterator for Filter<I, P>
where
P: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
// ...
}
这里重要的部分是type Item = I::Item
,这意味着I
(原始迭代器)的项类型被完全传递。没有添加参考。
剩下 .iter()
:这就是问题的原因。 Vec::iter
returns slice::Iter<T>
实现 Iterator
:
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
// ...
}
这里我们看到项目类型是对 T
(向量的元素类型)的引用。
解决方案
在一般情况下,您可以对迭代引用的任何迭代器调用 .cloned()
以获得按值迭代项目的新迭代器(通过克隆每个项目)。对于实现 Copy
的类型,您可以(并且应该)使用 .copied()
。例如。 range.iter().filter(|&n| *n % p != 0).copied().collect()
.
但是,在这种情况下有一个更好的解决方案:因为你之后不再需要向量,你可以调用 into_iter()
而不是 iter()
以便按值直接获取 u32
上的迭代器。这会消耗向量,使其之后无法访问。但是,如前所述,这在这里不是问题。
range = range.into_iter().filter(|&n| n % p != 0).collect();
另请注意,我删除了 *n
中的 *
,因为不再需要解除引用。
其他提示
总是重新分配一个新向量不是很快。 Eratosthenes 筛法通常以不同的方式实现:不是存储数字,而是只存储布尔值来表示每个数字是否为素数。这些数字永远不会显式存储,而是通过使用 vector/array 的索引隐式存储。
为了让它变得非常快,不应该使用 Vec<bool>
而应该使用专用的 bitvec。 Vec<bool>
每个 bool
存储一个字节,尽管只需要一位。提供这种位向量的事实上的箱子是 bit-vec
,它还方便地显示了埃拉托色尼筛法的示例实现 in its documentation。