Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't the comparison operation in my iterator filter over generic types work?

I am trying to understand why the following code does not compile (Playground):

fn inspection<I>(iter: I)
where 
    I: Iterator, 
    I::Item: Ord,
{
    let inspection = iter
        .filter(|x| x > 0);
}

fn main() {
    let data = vec![1, 2, 3];
    inspection(data.iter()); // or inspection(data.into_iter());
}

The error is:

error[E0308]: mismatched types
 --> src/main.rs:9:25
  |
9 |         .filter(|x| x > 0);
  |                         ^ expected reference, found integral variable
  |
  = note: expected type `&<I as std::iter::Iterator>::Item`
             found type `{integer}`

I tried to follow the various alternatives (by de-referencing the element) as explained here without success.

First attempt:

.filter(|x| **x > 0); 
error[E0614]: type `<I as std::iter::Iterator>::Item` cannot be dereferenced
  --> src/main.rs:13:21
   |
13 |         .filter(|x| **x > 0);
   |  

Second attempt:

.filter(|&x| *x > 0);
error[E0614]: type `<I as std::iter::Iterator>::Item` cannot be dereferenced
  --> src/main.rs:13:22
   |
13 |         .filter(|&x| *x > 0);
   |    

Why is the program not compiling?

like image 430
Nick Avatar asked Jul 07 '18 12:07

Nick


Video Answer


2 Answers

The biggest issue (the other problem was covered by Lukas) is that you are comparing the iterator's generic items with an integer while comparisons provided by PartialOrd/Ord only work between the same types:

pub trait Ord: Eq + PartialOrd<Self> {
    fn cmp(&self, other: &Self) -> Ordering;
    ...
}

In order for a type T to be comparable with a number (in this case 0), T must be a number and 0 needs to be of type T too. The num crate containing useful numeric traits can help here with its Zero trait that provides a generic 0:

extern crate num;

use num::Zero;

fn inspection<'a, I, T: 'a>(iter: I)
where
    I: Iterator<Item = &'a T>,
    T: Zero + PartialOrd // T is an ordered number
{
    let inspection = iter.filter(|&x| *x > T::zero()); // T::zero() is 0 of the same type as T
}

fn main() {
    let data = vec![1, 2, 3];
    inspection(data.iter());
}
like image 56
ljedrz Avatar answered Sep 23 '22 02:09

ljedrz


You were really close to solving the first problem! You tried:

  • filter(|x| x > 0)
  • filter(|x| **x > 0)
  • filter(|&x| *x > 0)

The solution is: filter(|x| *x > 0) or filter(|&x| x > 0).

To see why, let's take a close look at the error message again:

9 |         .filter(|x| x > 0);
  |                         ^ expected reference, found integral variable
  |
  = note: expected type `&<I as std::iter::Iterator>::Item`
             found type `{integer}`

It says that it expected the type &<I as std::iter::Iterator>::Item and got the type {integer}. This means that the difference is one reference. You cannot compare &i32 to i32. So you can remove the one & by using either * (the dereference operator) or the |&x| pattern (which dereferences the value too). But if you combine both, this has the same effect as **: it tries to dereferences twice. Which is not possible because the type only contains one reference.


However, it still doesn't compile due to a more complicated issue. You can read more about it here:

  • Trait for numeric functionality in Rust
like image 33
Lukas Kalbertodt Avatar answered Sep 25 '22 02:09

Lukas Kalbertodt