您的示例有多个错误,让我们一个一个地检查它们。
首先,您不能将dyn Trait 存储在堆栈上,因为它可以具有可变大小。 filter() 要求其回调为Sized,因此您不能指定dyn Trait。
我们可以通过使用Box<dyn Fn(&str) -> bool> 来解决这个问题:
pub fn search_lines<'a>(query: &str, content: &'a str, insensitive: bool) -> Vec<&'a str> {
let lines = content.lines();
let mut matches = Vec::<&str>::new();
let mut condition: Box<dyn Fn(&str) -> bool> =
Box::new(|line: &str| -> bool { line.contains(query) });
if insensitive {
condition =
Box::new(|line: &str| -> bool { line.to_lowercase().contains(&query.to_lowercase()) });
};
lines.filter(condition).for_each(|line| matches.push(line));
matches
}
然后我们得到另外两个错误,重要的是:
error[E0277]: expected a `FnMut<(&&str,)>` closure, found `dyn for<'r> Fn(&'r str) -> bool`
--> src/lib.rs:14:18
|
14 | lines.filter(condition).for_each(|line| matches.push(line));
| ^^^^^^^^^ expected an `FnMut<(&&str,)>` closure, found `dyn for<'r> Fn(&'r str) -> bool`
|
= help: the trait `FnMut<(&&str,)>` is not implemented for `dyn for<'r> Fn(&'r str) -> bool`
= note: required because of the requirements on the impl of `for<'r> FnMut<(&'r &str,)>` for `Box<dyn for<'r> Fn(&'r str) -> bool>`
会发生什么?
如果我们查看at filter()'s signature,我们会发现它需要Pwhere P: FnMut(&Self::Item) -> bool 类型的回调。重要的部分是过滤器没有给回调一个拥有的Self::Item,而是一个对它的引用,&Self::Item,因为我们需要在之后使用它。 Self::Item 这里是&str,所以&Self::Item 是&&str:
pub fn search_lines<'a>(query: &str, content: &'a str, insensitive: bool) -> Vec<&'a str> {
let lines = content.lines();
let mut matches = Vec::<&str>::new();
let mut condition: Box<dyn Fn(&&str) -> bool> =
Box::new(|line: &&str| -> bool { line.contains(query) });
if insensitive {
condition =
Box::new(|line: &&str| -> bool { line.to_lowercase().contains(&query.to_lowercase()) });
};
lines.filter(condition).for_each(|line| matches.push(line));
matches
}
这可行,但我们可以进一步改进它。
首先,for_each(|line| matches.push(line)) 绝对不是惯用语。你要的是collect():
pub fn search_lines<'a>(query: &str, content: &'a str, insensitive: bool) -> Vec<&'a str> {
let lines = content.lines();
let mut condition: Box<dyn Fn(&&str) -> bool> =
Box::new(|line: &&str| -> bool { line.contains(query) });
if insensitive {
condition =
Box::new(|line: &&str| -> bool { line.to_lowercase().contains(&query.to_lowercase()) });
};
lines.filter(condition).collect()
}
其次,我们可以使用一个小技巧来避免Box的堆分配:
pub fn search_lines<'a>(query: &str, content: &'a str, insensitive: bool) -> Vec<&'a str> {
let lines = content.lines();
let condition_intensive;
let mut condition: &dyn Fn(&&str) -> bool = &|line: &&str| -> bool { line.contains(query) };
if insensitive {
condition_intensive =
|line: &&str| -> bool { line.to_lowercase().contains(&query.to_lowercase()) };
condition = &condition_intensive;
};
lines.filter(condition).collect()
}
我们没有将回调存储在堆上,而是将其存储在堆栈中,有条件地。