'filter(func)' 和 'filter(|x| func(x))' 有什么区别?

  • 本文关键字:func filter 区别 rust iterator
  • 更新时间 :
  • 英文 :


filter(|x| func(x))filter(func)有什么区别?也许一个好的起点是了解如何使用类似于filter(|x| func(x))的语法编写filter(func)。我的代码如下所示:

fn filter_out_duplicates(vec_of_vecs: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
vec_of_vecs
.into_iter()
.filter(all_unique)
.collect()
}
pub fn all_unique<T>(iterable: T) -> bool
where
T: IntoIterator,
T::Item: Eq + Hash,
{
let mut unique = HashSet::new();
iterable.into_iter().all(|x| unique.insert(x))
}
error[E0599]: the method `collect` exists for struct `Filter<std::vec::IntoIter<Vec<u8>>, fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>}>`, but its trait bounds were not satisfied
--> src/main.rs:44:56
|
44 |             vec_of_vecs.into_iter().filter(all_unique).collect()
|                                                        ^^^^^^^ method cannot be called on `Filter<std::vec::IntoIter<Vec<u8>>, fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>}>` due to unsatisfied trait bounds
|
::: /.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/adapters/filter.rs:15:1
|
15 | pub struct Filter<I, P> {
| ----------------------- doesn't satisfy `_: Iterator`
|
= note: the following trait bounds were not satisfied:
`<fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>} as FnOnce<(&Vec<u8>,)>>::Output = bool`
which is required by `Filter<std::vec::IntoIter<Vec<u8>>, fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>}>: Iterator`
`fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>}: FnMut<(&Vec<u8>,)>`
which is required by `Filter<std::vec::IntoIter<Vec<u8>>, fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>}>: Iterator`
`Filter<std::vec::IntoIter<Vec<u8>>, fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>}>: Iterator`
which is required by `&mut Filter<std::vec::IntoIter<Vec<u8>>, fn(&Vec<u8>) -> bool {tmp::all_unique::<&Vec<u8>>}>: Iterator`
which is required by `&mut Filter<std::vec::IntoIter<Vec<Placement>>, fn(&Vec<Placement>) -> bool {all_unique::<&Vec<Placement>>}>: Iterator`

但是如果我使用|x| all_unique(x)代码,代码就会编译。我知道破译编译器错误是解决 Rust 问题的推荐方法,但我发现这个错误非常难以理解。

我发现一个讨论似乎对错误表示同情,而不是解释胁迫,但我发现Rustonomicon中关于胁迫的章节太短,无法提供理解。

本案与胁迫无关。这是后期绑定与早期绑定生命周期的另一种情况。

Rust 有两种生命周期:早期绑定后期绑定。区别归结为决定使用什么寿命。

对于后期绑定生命周期,你会得到一个更高等级的特征绑定 - 类似于for<'a> fn(&'a i32)。然后,仅在调用函数时选取生存期。

另一方面,对于早期绑定的一生,你会得到fn(&'some_concrete_lifetime i32).寿命可以推断,有时被省略,但它就在那里。它必须在我们决定函数指针/项的类型时决定。

filter()需要一个 HRTB 函数,即具有后期绑定生存期。这是因为FnMut(&Self::Item) -> bool的脱糖,即filter()结合的,是for<'a> FnMut(&'a Self::Item) -> bool的,或者,如果你愿意,for<'a> FnMut<(&'a Self::Item,), Output = bool>

但是,您的all_unique()是通用的,而不是T: IntoIterator。如果我们设定T = &'a Vec<u8>,那么'a是早期绑定的。这是因为泛型参数的生存期总是早期绑定的 - 本质上,因为我们不能后期绑定泛型参数,因为在 Rust 中没有办法表达for<T>,因为泛型类型参数是单态的,所以这通常是不可能的。

所以,如果我们揭示被忽略的寿命,你想满足性状束缚fn(&'some_lifetime Vec<u8>) -> bool: for<'all_lifetimes> FnMut(&'all_lifetimes Vec<u8>) -> bool,而这个束缚是假的。这就是您看到错误的原因。

但是,如果我们使用闭包,则会生成特定于类型的闭包&'lifetime Vec<u8>.由于它在类型上不是泛型的,因此生存期可以延迟绑定。

我不是 100% 确定这里发生了什么。根据您的观点,它甚至可以被视为编译器限制/错误,但我认为这就是发生的事情:

当你编写filter(all_unique)时,all_unique泛型函数,根据filter的定义,它被解析为对它正在迭代的项目的引用:

fn filter<P>(self, predicate: P) -> Filter<Self, P>ⓘ where
P: FnMut(&Self::Item) -> bool, 

所以你实际上是在打电话给all_unique<&Vec<u8>>.您可能认为一切正常,因为该&Vec<u8>实际上实现了IntoIterator和其他约束。

但问题在于寿命。看,当filter中存在约束P: FnMut(&Self::Item) -> bool实际上是P: for <'a> FnMut(&'a Self::Item) -> bool的句法糖时,即函数必须能够接受任何生存期,而您的函数不能。

但是,等等!你可能会说,你的函数all_unique<T>肯定可以T: 'a任何一辈子'a。这是真的,但这不是这里正在发生的事情:你是在召唤filter<P>P=all_unique::<&'f Vec<u8>>在那一世'f。那一辈子不是一辈子!现在,您的all_unique函数被特定的生命周期污染,并且不满足上述for <'a> ...

当然,您实际上并不需要for <a>',因为您正在调用具有适当生存期的函数,但语法就是它,它会强制错误。

显而易见的解决方案是编写all_unique以获取引用:

pub fn all_unique<T>(iterable: &T) -> bool

这实际上是完整的糖:

pub fn all_unique<'a, T>(iterable: &'a T) -> bool

'a的普遍性(for <'a>事物)是隐含的。

现在调用filter(all_unique)选择泛型all_unique::<Vec<u8>>,不受生命周期污染,可以用任何'a调用。

而这确实是一个语法限制,你可以写:

pub fn all_unique<T> (iterable: T) -> bool { /* ... */ }
pub fn all_unique_ref<'a, T> (iterable: &'a T) -> bool {
all_unique::<&T>(iterable)
}

filter(all_unique_ref)会起作用,而filter(all_unique)不会。

使用 lambda 表达式的解决方案:

filter(|x| all_unique(x))

就像那样all_unique_ref但匿名。

TL;博士;导致原始错误的原因是参数的生存期是在泛型函数的类型中捕获的,而不是在该函数的使用中捕获的。这让filter()不高兴,因为它的论点看起来不够笼统。

最新更新