feat(allocator): improve PartialEq for Vec (#23716)

Improve `PartialEq` implementations on `oxc_allocator::Vec`, bringing it up to parity with `std::vec::Vec`.

Previously code like this would fail to compile due to missing implementations for comparing `Vec`s to slices:

```rust
let v = Vec::from_array_in([1, 2, 3], &allocator);
assert_eq!(v, [1, 2, 3]);
```
This commit is contained in:
overlookmotel committed 2026-06-22 15:01:58 +00:00
1 parent cf5e47e42a
commit 9d1aa7fcb6
2 files changed
+152 -1

No files matched your search

+130 -1
View File
@@ -36,7 +36,7 @@ type InnerVec<'a, T> = InnerVecGeneric<'a, T, Arena>;
///
/// Static checks make this impossible to do. [`Vec::new_in`] and all other methods which create
/// a [`Vec`] will refuse to compile if called with a [`Drop`] type.
#[derive(PartialEq, Eq)]
#[derive(Eq)]
#[repr(transparent)]
pub struct Vec<'alloc, T>(InnerVec<'alloc, T>);
@@ -249,6 +249,67 @@ impl<'alloc, T> ops::DerefMut for Vec<'alloc, T> {
}
}
// Forward all `PartialEq` comparisons to the inner `Vec`, mirroring the set of impls it provides
// (against another `Vec`, slices, and arrays). These are implemented on the wrapper directly because
// trait resolution does not look through `Deref`.
//
// The `Vec`-vs-`Vec` impl takes the place of `#[derive(PartialEq)]`. The derive would only allow
// comparing two `Vec`s with the same element type `T`, whereas this allows comparing `Vec`s with
// different (but comparable) element types, matching the inner `Vec` and `std::vec::Vec`.
impl<T: PartialEq<U>, U> PartialEq<Vec<'_, U>> for Vec<'_, T> {
#[inline]
fn eq(&self, other: &Vec<'_, U>) -> bool {
self.0 == other.0
}
}
macro_rules! impl_slice_partial_eq {
($rhs:ty) => {
impl<T: PartialEq<U>, U> PartialEq<$rhs> for Vec<'_, T> {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
self.0 == *other
}
}
};
}
impl_slice_partial_eq!([U]);
impl_slice_partial_eq!(&[U]);
impl_slice_partial_eq!(&mut [U]);
macro_rules! impl_array_partial_eq {
($rhs:ty) => {
impl<T: PartialEq<U>, U, const N: usize> PartialEq<$rhs> for Vec<'_, T> {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
self.0 == *other
}
}
};
}
impl_array_partial_eq!([U; N]);
impl_array_partial_eq!(&[U; N]);
impl_array_partial_eq!(&mut [U; N]);
// Reverse direction: slice on the left, `Vec` on the right (e.g. `&[T] == vec`), forwarding to the
// inner `Vec`'s reverse impls. `std::vec::Vec` provides these, so mirror them here.
macro_rules! impl_slice_partial_eq_reverse {
($lhs:ty) => {
impl<T: PartialEq<U>, U> PartialEq<Vec<'_, U>> for $lhs {
#[inline]
fn eq(&self, other: &Vec<'_, U>) -> bool {
*self == other.0
}
}
};
}
impl_slice_partial_eq_reverse!([T]);
impl_slice_partial_eq_reverse!(&[T]);
impl_slice_partial_eq_reverse!(&mut [T]);
impl<'alloc, T> IntoIterator for Vec<'alloc, T> {
type IntoIter = <InnerVec<'alloc, T> as IntoIterator>::IntoIter;
type Item = T;
@@ -400,6 +461,74 @@ mod test {
assert_eq!(s, r#"["x"]"#);
}
#[test]
#[expect(clippy::op_ref)]
fn vec_partial_eq() {
let allocator = Allocator::default();
let v = Vec::from_array_in([1, 2, 3], &allocator);
let same = Vec::from_array_in([1, 2, 3], &allocator);
// `Vec` vs `Vec` (same element type), by value and by reference.
assert!(v == same);
assert_eq!(v, same);
assert!(&v == &same);
// `Vec` vs owned array `[U; N]`, and references to it.
assert!(v == [1, 2, 3]);
assert_eq!(v, [1, 2, 3]);
assert!(v == &[1, 2, 3]);
assert!(v == &mut [1, 2, 3]);
// `Vec` vs slice `&[U]` / `&mut [U]`.
let slice: &[i32] = &[1, 2, 3];
assert!(v == slice);
let mut_slice: &mut [i32] = &mut [1, 2, 3];
assert!(v == mut_slice);
// `Vec` vs unsized slice `[U]` (reached by dereferencing a slice reference).
assert!(v == *slice);
// Reverse direction: slice on the left, `Vec` on the right (std parity).
// Note: arrays on the left (`[1, 2, 3] == v`) are not supported - `std` doesn't provide
// `[T; N]: PartialEq<Vec>` either, only the slice forms below.
assert!(&[1, 2, 3][..] == v);
assert!(slice == v);
assert!(mut_slice == v);
assert!(*slice == v);
// Method-call form (no auto-ref). `v.eq(slice)` resolves through the unsized `[U]` impl.
assert!(v.eq(slice));
assert!(v.eq(&same));
assert!(v.eq(&[1, 2, 3]));
assert!(slice.eq(&v));
// Inequality still works.
assert!(v != [1, 2, 4]);
assert!(v != Vec::from_array_in([1, 2], &allocator));
// Cross element type: `T: PartialEq<U>` where `T != U`.
#[expect(clippy::items_after_statements)]
#[derive(Clone, Copy)]
struct Foo(u8);
#[derive(Clone, Copy)]
struct Bar(u8);
impl PartialEq<Bar> for Foo {
fn eq(&self, other: &Bar) -> bool {
self.0 == other.0
}
}
let foos = Vec::from_array_in([Foo(1), Foo(2)], &allocator);
let bars = Vec::from_array_in([Bar(1), Bar(2)], &allocator);
assert!(foos == bars);
assert!(foos == [Bar(1), Bar(2)]);
let bars_slice: &[Bar] = &[Bar(1), Bar(2)];
assert!(foos == bars_slice);
}
#[test]
fn lifetime_variance() {
fn _assert_vec_variant_lifetime<'a: 'b, 'b, T>(program: Vec<'a, T>) -> Vec<'b, T> {
+22
View File
@@ -2339,6 +2339,7 @@ macro_rules! __impl_slice_eq1 {
}
__impl_slice_eq1! { Vec<'_, U, A2>, A2: Alloc }
__impl_slice_eq1! { [U] }
__impl_slice_eq1! { &[U] }
__impl_slice_eq1! { &mut [U] }
@@ -2361,6 +2362,27 @@ __impl_slice_eq1_array! { [U; N] }
__impl_slice_eq1_array! { &[U; N] }
__impl_slice_eq1_array! { &mut [U; N] }
// Reverse direction: slice/`Vec` on the left-hand side, `Vec` on the right (e.g. `&[T] == vec`).
// `std::vec::Vec` provides these, so mirror them here.
macro_rules! __impl_slice_eq1_reverse {
($Lhs: ty) => {
impl<T, U, A> PartialEq<Vec<'_, U, A>> for $Lhs
where
T: PartialEq<U>,
A: Alloc,
{
#[inline]
fn eq(&self, other: &Vec<'_, U, A>) -> bool {
self[..] == other[..]
}
}
};
}
__impl_slice_eq1_reverse! { [T] }
__impl_slice_eq1_reverse! { &[T] }
__impl_slice_eq1_reverse! { &mut [T] }
/// Implements comparison of vectors, lexicographically.
impl<'a, T: 'a + PartialOrd, A: Alloc, A2: Alloc> PartialOrd<Vec<'a, T, A2>> for Vec<'a, T, A> {
#[inline]