-1

I need to count the length of a vector of (bool, i32) where if the bool is true I increment count. I'm using fold to do this:

fn main() {
    let domain = [(true, 1), (false, 2), (true, 3)];
    let dom_count = domain.iter()
        .fold(0, |count, &(exists, _)| if exists {count + 1});
    println!("dom_count: {}", dom_count);
}

The compiler complained saying:

.fold(0, |count, &(exists, _)| if exists {count + 1})
                               ^^^^^^^^^^^^^^^^^^^^^ expected (), found integral variable

So I added a ; and got this:

.fold(0, |count, &(exists, _)| if exists {count + 1;})
                               ^^^^^^^^^^^^^^^^^^^^^^ expected integral variable, found ()

How do you correctly use an if statement inside of fold?

HiDefender
  • 2,088
  • 2
  • 14
  • 31

1 Answers1

4

What value are you attempting to use when the if condition is false?

That's what the compiler is telling you first. Because there's no else clause, the return type of the missing clause must be (). And since both the true and false branches of the if must have the same type, the true branch must return (). However, your true branch is trying to return a number.

By adding the ;, you made it so that both branches of the if returned (), which then failed because your fold is supposed to return an integer.

One solution is to return a value in the else clause:

fn main() {
    let domain = [(true, 1), (false, 2), (true, 3)];

    let dom_count = domain.iter()
        .fold(0, |count, &(exists, _)| {
            if exists {
                count + 1
            } else {
                count
            }
        });

    println!("dom_count: {}", dom_count);
}

Or

fn main() {
    let domain = [(true, 1), (false, 2), (true, 3)];

    let dom_count = domain.iter()
        .fold(0, |count, &(exists, _)| {
            count + if exists {
                1
            } else {
                0
            }
        });

    println!("dom_count: {}", dom_count);
}

It's much more idiomatic to use filter:

fn main() {
    let domain = [(true, 1), (false, 2), (true, 3)];

    let dom_count = domain.iter()
        .filter(|&&(exists, _)| exists)
        .fold(0, |count, _| count + 1);

    println!("dom_count: {}", dom_count);
}

And the act of counting the the number of items is already handled by Iterator::count:

fn main() {
    let domain = [(true, 1), (false, 2), (true, 3)];

    let dom_count = domain.iter().filter(|&&(exists, _)| exists).count();

    println!("dom_count: {}", dom_count);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thank you. I had no idea that if-statements behaved like that. Also I apologize about the lack of MCVE, I cleaned it up. On a related note, why does Rust allow single line if statements to lack `;` if they are not returning a value? For instance it `count` is a variable why would this compile: `if true {count = 1}`? Shouldn't you be required to add a semi-colon: `if true {count = 1;}`? – HiDefender Nov 28 '16 at 06:04
  • @HiDefender *if they are not returning a value* — they **do** return a value - the value `()` of the type `()`. This is the empty tuple, sometimes called the "unit type". – Shepmaster Nov 28 '16 at 13:36