0

How can I reduce an Iterator of booleans using the && operator? Here is an example (just the relevant part):

let row: Vec<(u32, bool)> = vec![(12, true), (13, false), (15, true)];

row.iter()
    .map(|it| {
        let (_, marked) = it;
        marked
    })
    .reduce(|acc, mk | acc && mk);

The compiler recommends dereferencing acc first, then dereferencing mk, and finally borrowing the whole expression. It's a quite simple operation but I'm unable to find the Rustacean way to do it.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
salpreh
  • 190
  • 2
  • 6
  • 1
    Does this answer your question? [What is the difference between iter and into\_iter?](https://stackoverflow.com/questions/34733811/what-is-the-difference-between-iter-and-into-iter) – Colonel Thirty Two Feb 13 '22 at 02:25
  • Not exaclty. Using into_iter to iterate in this case still yielding references to bool in map operation. – salpreh Feb 13 '22 at 02:37
  • 2
    @salpreh [It doesn't](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4ad74dbe2cd5612e25bd1e1a31e9c497). – Chayim Friedman Feb 13 '22 at 03:02
  • You're right @ChayimFriedman, sorry Colonel Thirty Two. When I tested that option agains my full code (which iterates agains a ref to a Vec unwrapped from an Optional) I ended up with the same compiler error. But that solution totally works in the isolated case I posted. – salpreh Feb 13 '22 at 03:22
  • 6
    I'd recommend using [`all`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.all) or [`any`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.any), which, unlike `fold` or `reduce`, provide short-circuiting behavior. – Francis Gagné Feb 13 '22 at 03:45
  • Unrelated to your problem, but your call to `map` could be tidied up: `.map(|it| { it.1 })` – Chris Feb 14 '22 at 00:09
  • 1
    @Chris And even `.map(|it| it.1)`, or `.map(|(_, marked)| marked)`. – Chayim Friedman Feb 14 '22 at 00:10

4 Answers4

2

Here's an alternative solution that uses fold to specify an initial value:

let val: bool = row.iter()
    .map(|it| {
        let (_, marked) = it;
        marked
    })
    .fold(true, |acc, mk| acc && *mk);

The reason acc && mk doesn't work is because you have an iterator over &bool but you want a bool. *acc && *mk doesn't work because the accumulator is of type bool, so you want acc && *mk. However, you can no longer use reduce since the accumulator and values aren't the same type (bool vs &bool), hence the need for fold.

Also note that fold and map can be combined:

let val: bool = row.iter()
    .fold(true, |acc, (_, mk)| acc && *mk);
Aplet123
  • 33,825
  • 1
  • 29
  • 55
2

Because There are three common methods which can create iterators from a collection;

iter(), which iterates over &T.
iter_mut(), which iterates over &mut T.
into_iter(), which iterates over T.

Your code uses iter (), so the iteration is & type, so you need to use & to extract value

Maybe you can do it like this:

    let row: Vec<(u32, bool)> = vec![(12, true), (13, false), (15, true)];

    let op = row.iter()
        .map(|&it| {
            let (_, marked) = it;
            marked // No dereference is required
        })
        .reduce(|acc, mk| acc && mk);
    println!("{:?}",op);

or

    let row: Vec<(u32, bool)> = vec![(12, true), (13, false), (15, true)];

    let op = row.into_iter()
        .map(|it| {
            let (_, marked) = it;
            marked // No dereference is required
        })
        .reduce(|acc, mk| acc && mk);
    println!("{:?}",op);
iwetuan
  • 21
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 13 '22 at 06:17
1

I think the cleanest is below. Using this reference pattern; you can also destructure the tuple inline, whereas fold is a bit like reduce, except that you won't get constrained to Option<Self::Item> as return type, and have to provide an initial value, etc.

let result = row
    .iter()
    .map(|(_, marked)| marked)
    .fold(true, |acc, &mk| acc && mk);

result will be of type bool.

Riwen
  • 4,734
  • 2
  • 19
  • 31
0

After trying things the best solution I found is dereferencing value returned in previous map, so instead of returning a reference to boolean returns an owned copy. Here is the modified question code:

let row: Vec<(u32, bool)> = vec![(12, true), (13, false), (15, true)];

row.iter()
    .map(|it| {
        let (_, marked) = it;
        *marked // Dereference here
    })
    .reduce(|acc, mk | acc && mk);
salpreh
  • 190
  • 2
  • 6