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); } }; }
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
)?
source to share
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]
andVec<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!
source to share
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.
source to share