简体   繁体   English

如何捕获Rust闭包范围之外的变量?

[英]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: 但是,以上内容似乎在从周围的范围捕获ss_sorted遇到问题,因为在编译时出现以下错误:

error: type mismatch: the type [closure@src/lib.rs:23:25: 32: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) 错误:类型不匹配:类型[closure@src/lib.rs:23:25: 32:6 s_sorted:_, s:_]实现了for<'r> core::ops::FnMut<(&'r str,)> ,但必须使用trait core::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 a Fn 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... 另外...

  1. You don't need to restrict yourself to 'static strings: 您无需将自己限制为'static字符串”:

     pub fn anagrams_for<'a>(s: &str, v: &[&'a str]) -> Vec<&'a str> { 
  2. It doesn't need to be a move closure. 它并不需要是一个move关闭。

  3. Extract the logic of creating the vector of sorted chars. 提取创建排序字符向量的逻辑。 This helps ensure that the logic stays consistent between the two and means you don't have to declare those vectors as mutable for longer than needed. 这有助于确保逻辑保持两者之间的一致意味着你不必声明为可变比需要更长的载体。
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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM