zerkms
zerkms

Reputation: 255145

How to help infer the type for generic vector when comparing equality

I have the following code:

struct HeadTail<T>(T, Vec<T>);

fn head_tail<T : Clone>(v: &Vec<T>) -> Option<HeadTail<T>> {
    match v.len() {
        0 => None,
        _ => {
            let mut tmp_v = v.clone();
            let head = tmp_v.remove(0);
            Some(HeadTail(head, tmp_v))
        }
    }
}

#[test]
fn head_tail_many() {
    let vec = vec![1, 2, 3, 4];
    let result = head_tail(&vec);

    match result {
        None => unreachable!(),
        Some(HeadTail(head, tail)) => {
            assert_eq!(1, head);
            assert_eq!(3, tail.len());
            assert_eq!([2, 3, 4], tail);
        }
    };
}

Online demo with the problem

And it fails with the following exception:

<std macros>:5:8: 5:33 error: the trait `core::cmp::PartialEq<collections::vec::Vec<_>>` is not implemented for the type `[_; 3]` [E0277]
<std macros>:5 if ! ( * left_val == * right_val ) {

Why can Rust not infer the type in this case?

What can I do to let it know it's any numeric type (eg u8)?

Upvotes: 3

Views: 565

Answers (2)

Shepmaster
Shepmaster

Reputation: 432139

It's useful to create a MCVE when debugging these types of things. Here's an example:

fn main() {
    let vec = vec![1, 2, 3, 4];
    assert_eq!([1,2,3,4], vec);
}

With the error

<std macros>:5:8: 5:33 error: the trait `core::cmp::PartialEq<collections::vec::Vec<_>>` is not implemented for the type `[_; 4]` [E0277]
<std macros>:5 if ! ( * left_val == * right_val ) {
                      ^~~~~~~~~~~~~~~~~~~~~~~~~

So, there's some kind of error with PartialEq, let's try to reduce further:

fn main() {
    let vec = vec![1, 2, 3, 4];
    [1,2,3,4] == vec;
}

With the same basic error:

<anon>:3:5: 3:21 error: the trait `core::cmp::PartialEq<collections::vec::Vec<_>>` is not implemented for the type `[_; 4]` [E0277]
<anon>:3     [1,2,3,4] == vec;
             ^~~~~~~~~~~~~~~~

The _ in Vec<_> means an as-yet-undermined type. Let's use explicit types to see if that's the problem:

fn main() {
    let vec = vec![1u8, 2, 3, 4];
    [1u8,2,3,4] == vec;
}

Nope, still the same error:

<anon>:3:5: 3:23 error: the trait `core::cmp::PartialEq<collections::vec::Vec<u8>>` is not implemented for the type `[u8; 4]` [E0277]
<anon>:3     [1u8,2,3,4] == vec;
             ^~~~~~~~~~~~~~~~~~

Let's try flipping things around:

fn main() {
    let vec = vec![1u8, 2, 3, 4];
    vec == [1u8,2,3,4];
}

Hmm. This works! Swapping the order in your original code also works.

Of course, the big question left is why. Let's look at the docs for Vec, specifically the section about PartialEq:

impl<'a, 'b, A, B> PartialEq<[B; 4]> for Vec<A>
    where A: PartialEq<B>
{
    fn eq(&self, other: &[B; 4]) -> bool { ... }
}

So, you can test a Vec<A> to an &[B; 4] for equality, if you can test A and B for equality. What about the other way around? The docs for arrays don't mention Vec at all (which makes sense, as they are more of a core feature), and there aren't any inverse implementations of PartialEq. This certainly seems surprising, and I have no good explanation for why they aren't there...

Ah, it appears this happened in this commit. Here's the commit message:

The primary implementation which was lost was the ability to compare &[T] and Vec<T> (in that order).

This change also modifies the assert_eq! macro to not consider both directions of equality, only the one given in the left/right forms to the macro. This modification is motivated due to the fact that &[T] == Vec<T> no longer compiles, causing hundreds of errors in unit tests in the standard library (and likely throughout the community as well).

Manishearth found a comprehensive blog post that describes the rationale behind this change in good detail!

Upvotes: 5

Manishearth
Manishearth

Reputation: 16198

To expand on @Shepmaster's answer, the issue here is that the implementation for the == operator is asymmetric in this case. We have an impl PartialEq<Vec<T>> for [T; n], but not the reverse. Perhaps we should have a reverse implementation, but generic array types aren't well supported yet.

This wasn't an issue with inference at all, it was an issue of comparing two different types and Rust not having a symmetric implementation.

Upvotes: 2

Related Questions