Logicrat
Logicrat

Reputation: 4468

Argument to for_each() function appears to be the wrong type

I'm working thru some programming exercises on Exercism when I ran into this problem. It's not clear to me why the type of the closure set to for_each() even matters.

Here's the entire Rust program:

use std::collections::HashSet;

// reformat(word) returns a tuple mapping its argument to (lowercase, sorted_lowercase)
fn reformat(word: &str) -> (String,String) {
    let lower = word.to_lowercase();
    let mut char_vec : Vec<char> = lower.chars().collect();
    char_vec.sort_unstable();
    let sorted : String = char_vec.iter().collect();
    (lower,sorted)
}

// Items in 'possible_anagrams' will be added to the set if they contain all of the
// same characters as 'word' but arranged in a different order.
fn anagrams_for<'a>(word: &str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> {
    let mut set = HashSet::<&str>::new();
    let w = reformat(word);
    let is_anagram = |x:&str|->bool{let t=reformat(x);w.1==t.1&&w.0!=t.0};
    possible_anagrams.iter().filter(|x|is_anagram(x)).for_each(|x| set.insert(x));
    set
}

fn main() {
    let a : [&str; 4] = ["FRBA", "Braf", "wut", "batt"];
    println!("{:#?}", anagrams_for("Frab", &a));
}

I get an error message about the argument to the for_each() here:

       |
    18 |     possible_anagrams.iter().filter(|x|is_anagram(x)).for_each(|x| set.insert(x));
       |                                                                    ^^^^^^^^^^^^^ 
expected `()`, found `bool`

The error message is not at all clear to me. I've tried various remedies but any change I make seems to make matters worse, i.e., more error messages.

I have a completely different manifestation of the anagrams_for() function that does work properly. So as far as the coding exercise goes, I have solved it via the following version of this function:

pub fn anagrams_for<'a>(word: &str, possible_anagrams: &'a[&str]) -> HashSet<&'a str> {
    let mut set = HashSet::<&str>::new();
    let w = reformat(word);
    for x in possible_anagrams {
        let test = reformat(x);
        if w.1 == test.1 && w.0 != test.0 {
            set.insert(x);
        }
    }
    set
}

This one functions as I want. I've included it here as an example of what I want the so-far-not-working code to do.

Upvotes: 2

Views: 298

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 70850

HashSet::insert() returns bool (true if the value was already in the set).

Iterator::for_each() expects its closure to return the unit type (), or "nothing" (void in C).

Rust is expression-oriented: (almost) everything is an expression. Closures (and functions) returns the value of their last expression. That is, || expr is the same as || { return expr; }, and thus your closure returns the return value of insert() - a bool, while for_each() expects it to return ().

The fix is simple: you just need to discard insert()'s return value. It is done by making it into a statement, by appending a semicolon to it. This will also require you to use a block:

possible_anagrams.iter().filter(|x| is_anagram(x)).for_each(|x| { set.insert(x); });

Upvotes: 5

Related Questions