[英]How do I capture variables outside the scope of a closure in Rust?
I'm writing a case-insensitive anagram finder given a word and a list of words. 我正在写一个不区分大小写的字谜查找器,给出一个单词和一个单词列表。 I have the following code:
我有以下代码:
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut s_sorted: Vec<_> = s.to_lowercase().chars().collect();
s_sorted.sort();
v.iter().filter_map(move |word: &str| {
let mut word_sorted: Vec<_> = word.to_lowercase().chars().collect();
word_sorted.sort();
if word_sorted == s_sorted && s.to_lowercase() != word.to_lowercase() {
Some(word)
} else {
None
}
}).collect()
}
The logic of this is to sort the lowercase given word, and for each word in the vector, do the same. 这样做的逻辑是对给定单词的小写字母进行排序,并对向量中的每个单词执行相同的操作。 If the words are different pre-sort (to eliminate self-anagrams) but are the same post-sort, add it to the output.
如果单词是不同的预排序(以消除自字谜),但它们是相同的后排序,则将其添加到输出中。
The above seems to have problems capturing s
and s_sorted
from the surrounding scope, though, because when I compile I get the following error: 但是,以上内容似乎在从周围的范围捕获
s
和s_sorted
遇到问题,因为在编译时出现以下错误:
error: type mismatch: the type
[closure@src/lib.rs:23:25: 32:6 s_sorted:_, s:_]
implements the traitfor<'r> core::ops::FnMut<(&'r str,)>
, but the traitcore::ops::FnMut<(&&str,)>
is required (expected &-ptr, found str)错误:类型不匹配:类型
[closure@src/lib.rs:23:25: 32:6 s_sorted:_, s:_]
实现了for<'r> core::ops::FnMut<(&'r str,)>
,但必须使用traitcore::ops::FnMut<(&&str,)>
(期望&-ptr,找到str)
When I looked at the description of this error type ([E0281]), I found the following TL;DR: 当我查看此错误类型([E0281])的描述时,发现以下TL; DR:
The issue in this case is that
foo
is defined as accepting aFn
with no arguments, but the closure we attempted to pass to it requires one argument.在这种情况下,问题在于
foo
被定义为接受不带参数的Fn
,但是我们尝试传递给它的闭包需要一个参数。
This is confusing because I thought move closures capture variables from the surrounding scope. 这令人困惑,因为我认为移动闭包可以捕获周围范围的变量 。
What am I missing? 我想念什么?
This doesn't have anything to do with capturing variables in the closure. 这与捕获闭包中的变量没有任何关系。 Let's check out the error message again, reformatted a bit:
让我们再次检查错误消息,重新格式化:
type mismatch:
the type `[closure@<anon>:5:25: 14:6 s_sorted:_, s:_]`
implements the trait `for<'r> core::ops::FnMut<(&'r str,)>`,
but the trait `core::ops::FnMut<(&&str,)>` is required
(expected &-ptr, found str)
And more clearly: 更清楚地:
found: for<'r> core::ops::FnMut<(&'r str,)>
expected: core::ops::FnMut<(&&str,)>
And zooming in even further: 并进一步放大:
found: &'r str
expected: &&str
The culprit is this: |word: &str|
罪魁祸首是:
|word: &str|
. 。
You have declared that your closure accepts a string slice, but that's not what the iterator yields . 您已经声明闭包接受字符串切片,但这不是迭代器产生的结果 。
v
is a slice of &str
, and an iterator over a slice returns references to the items in the slice. v
是&str
的切片,并且切片上的迭代器返回对该切片中各项的引用。 Each iterator element is a &&str
. 每个迭代器元素都是一个
&&str
。
Change your closure to |&word|
将您的闭包更改为
|&word|
and it will work. 它会工作。 This uses pattern matching to dereference the closure argument once before the value is bound to
word
. 在值绑定到
word
之前,这将使用模式匹配来取消引用闭包参数。 Equivalently (but less idiomatically), you could use |word|
等效地(但习惯用法较少),您可以使用
|word|
and then *word
inside the closure. 然后在闭包内输入
*word
。
Additionally... 另外...
You don't need to restrict yourself to 'static
strings: 您无需将自己限制为
'static
字符串”:
pub fn anagrams_for<'a>(s: &str, v: &[&'a str]) -> Vec<&'a str> {
It doesn't need to be a move
closure. 它并不需要是一个
move
关闭。
fn sorted_chars(s: &str) -> Vec<char> {
let mut s_sorted: Vec<_> = s.to_lowercase().chars().collect();
s_sorted.sort();
s_sorted
}
pub fn anagrams_for<'a>(s: &str, v: &[&'a str]) -> Vec<&'a str> {
let s_sorted = sorted_chars(s);
v.iter().filter_map(|&word| {
let word_sorted = sorted_chars(word);
if word_sorted == s_sorted && s.to_lowercase() != word.to_lowercase() {
Some(word)
} else {
None
}
}).collect()
}
fn main() {}
The issue here is that you are attempting to create a Vec<&'static str>
from a sequence of &&'static str
. 这里的问题是,您试图从
&&'static str
序列创建Vec<&'static str>
。
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut s_sorted: Vec<_> = s.to_lowercase().chars().collect();
s_sorted.sort();
v.iter().cloned().filter_map(|word: &'static str| {
let mut word_sorted: Vec<_> = word.to_lowercase().chars().collect();
word_sorted.sort();
if word_sorted == s_sorted && s.to_lowercase() != word.to_lowercase() {
Some(word)
} else {
None
}
}).collect()
}
The cloned
call is necessary to go from a &&'static str
to a &'static str
. cloned
调用对于从&&'static str
到&'static str
是必要的。 This is an inexpensive operation since a &str
is just a pointer to some utf8 sequence, plus a length. 这是一种廉价的操作,因为
&str
只是指向某些utf8序列的指针,再加上一个长度。
Edit: actually an even better solution is to clone as late as possible 编辑:实际上,一个更好的解决方案是尽可能晚地克隆
v.iter().filter_map(move |word: &&'static str| { // <--- adapted the type to what is actually received
let mut word_sorted: Vec<_> = word.to_lowercase().chars().collect();
word_sorted.sort();
if word_sorted == s_sorted && s.to_lowercase() != word.to_lowercase() {
Some(word.clone()) // <--- moved clone operation here
} else {
None
}
}).collect()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.