Generic integer problem

I feel like this should work across all integers, but I can't get it to compile...

use num::Integer;

struct EvenOddVec<T> {
    even: Vec<T>,
    odd: Vec<T>,
}

impl<T> From<Vec<T>> for EvenOddVec<T>
where
    T: Integer,
{
    fn from(input: Vec<T>) -> Self {
        let new = Self {
            even: Vec::new(),
            odd: Vec::new(),
        };
        for i in input {
            if (i % 2) == 0 {
                new.even.push(i);
            } else {
                new.odd.push(i);
            }
        }
        new
    }
}

fn main() {
    let a = EvenOddVec::from(vec![-2, -1, 0, 1, 2, 3, 4, 5]);
    println!("{:?} - {:?}", a.even, a.odd);
}

Should compile but doesn't:

error[E0308]: mismatched types
  --> src/main.rs:18:21
   |
8  | impl<T> From<Vec<T>> for EvenOddVec<T>
   |      - expected this type parameter
...
18 |             if (i % 2) == 0 {
   |                 -   ^ expected type parameter `T`, found integer
   |                 |
   |                 expected because this is `T`
   |
   = note: expected type parameter `T`
                        found type `{integer}`

error[E0308]: mismatched types
  --> src/main.rs:18:27
   |
8  | impl<T> From<Vec<T>> for EvenOddVec<T>
   |      - expected this type parameter
...
18 |             if (i % 2) == 0 {
   |                -------    ^ expected type parameter `T`, found integer
   |                |
   |                expected because this is `T`
   |
   = note: expected type parameter `T`
                        found type `{integer}`

For more information about this error, try `rustc --explain E0308`.

Playground

You can convert the integer literals to T if you add a i32: Into<T> bound:

use num::Integer;

struct EvenOddVec<T> {
    even: Vec<T>,
    odd: Vec<T>,
}

impl<T> From<Vec<T>> for EvenOddVec<T>
where
    T: Integer + Copy,
    i32: Into<T>,
{
    fn from(input: Vec<T>) -> Self {
        let mut new = Self {
            even: Vec::new(),
            odd: Vec::new(),
        };
        for i in input {
            if (i % 2.into()) == 0.into() {
                new.even.push(i);
            } else {
                new.odd.push(i);
            }
        }
        new
    }
}

fn main() {
    let a = EvenOddVec::from(vec![-2, -1, 0, 1, 2, 3, 4, 5]);
    println!("{:?} - {:?}", a.even, a.odd);
}

Playground.

2 Likes

num::Integer has a is_even() method, which seems like what you need.

2 Likes

The num::Integer trait can be implemented by more than just the built-in integer types. For example, the following starting point could be built out into a valid implementation of Integer, given implementations of the appropriate methods:

enum ModTwoIntegers {
  Zero,
  One,
}

impl num::Integer for ModTwoIntegers {
    /* .. */
}

On the other hand, the literal 2 can only be interpreted as one of the built-in integer types (and not, for example, as a value of type ModTwoIntegers). The compiler therefore cannot figure out how you intend to guarantee that i % 2 is valid, since you've made no assertions about the relationships between T and the built-in integer types.

@jofas' solution adds one possible solution by asserting that T implements Integer and Copy (as you did), and additionally asserting that i32 can be converted to T using Into.

Another way to address this would be to replace the operation i % 2 with the Integer::is_even trait method.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.