How to help type inference for a 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 of the problem

And it doesn't work 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't Rust deduce the type in this case?

What can I do to tell it any digital type (for example u8

)?

+3


source to share


2 answers


It is useful to create an 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 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, some kind of error with PartialEq

, try to reduce it even 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;
             ^~~~~~~~~~~~~~~~

      

_

c Vec<_>

means not yet reinforced type. Let's use explicit types to see if this is the problem:

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

      

No, 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;
             ^~~~~~~~~~~~~~~~~~

      

Try to flip things:

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

      

Hmm. It works! It also performs order replacement in your source code.

Of course, the big question is why . Let's take a look at the docs forVec

, specifically the section on 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 Vec<A>

on &[B; 4]

for equality, if you can check A

and B

equality. What about the other way? The docs for arrays don't mention at all Vec

(which makes sense since they are more of a main function) and there are no reverse implementations PartialEq

. This certainly seems awesome and I have no good explanation as to why they are not there ...

And it looks like it happened in this commit . Here's the commit message:

The primary implementation that was lost was the ability to compare &[T]

and Vec<T>

(in that order).

This change also changes the macro assert_eq!

to ignore both directions of equality, only the one specified in the left / right form for the macro. This modification is motivated by the fact that it &[T] == Vec<T>

no longer compiles, causing hundreds of unit tests in the standard library (and probably throughout the community).

Manishearth found a comprehensive blog post detailing the rationale behind this change in detail!

+5


source


To expand on @ Shepmaster's answer, the problem here is that the implementation for the operator is ==

asymmetric in this case. We have impl PartialEq<Vec<T>> for [T; n]

, but not vice versa. Perhaps we should have a reverse implementation, but generic array types are not supported yet.



This was not a problem with inference at all, it was a problem of comparing two different types, and Rust did not have a symmetric implementation.

+2


source







All Articles