Skip to content

Commit

Permalink
Rollup merge of rust-lang#43011 - qnighy:unsized-tuple-impls, r=aturon
Browse files Browse the repository at this point in the history
Implement Eq/Hash/Debug etc. for unsized tuples.

As I mentioned in [the comment in rust-lang#18469](rust-lang#18469 (comment)), the implementations of `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Debug`, `Hash` can be generalized to unsized tuples.

This is consistent with the `derive` behavior for unsized structs.

```rust
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
struct MyTuple<X, Y, Z: ?Sized>(X, Y, Z);

fn f(x: &MyTuple<i32, i32, [i32]>) {
    x == x;
    x < x;
    println!("{:?}", x);
}
```

Questions:

- Need an RFC?
- Need a feature gate? I don't think it does because the unsized tuple coercion rust-lang#42527 is feature-gated.
- I changed `builder.field($name);` into `builder.field(&$name);` in the `Debug` implementation to pass compilation. This won't affect the behavior because `Debug for &'a T` is a mere redirection to `Debug for T`. However, I don't know if it affects code size / performance.
  • Loading branch information
Mark-Simulacrum authored Jul 12, 2017
2 parents 1a7dc0a + 728d26c commit e5288c9
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 7 deletions.
9 changes: 7 additions & 2 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1627,13 +1627,13 @@ macro_rules! tuple {
() => ();
( $($name:ident,)+ ) => (
#[stable(feature = "rust1", since = "1.0.0")]
impl<$($name:Debug),*> Debug for ($($name,)*) {
impl<$($name:Debug),*> Debug for ($($name,)*) where last_type!($($name,)+): ?Sized {
#[allow(non_snake_case, unused_assignments, deprecated)]
fn fmt(&self, f: &mut Formatter) -> Result {
let mut builder = f.debug_tuple("");
let ($(ref $name,)*) = *self;
$(
builder.field($name);
builder.field(&$name);
)*

builder.finish()
Expand All @@ -1643,6 +1643,11 @@ macro_rules! tuple {
)
}

macro_rules! last_type {
($a:ident,) => { $a };
($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) };
}

tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, }

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
7 changes: 6 additions & 1 deletion src/libcore/hash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ mod impls {

( $($name:ident)+) => (
#[stable(feature = "rust1", since = "1.0.0")]
impl<$($name: Hash),*> Hash for ($($name,)*) {
impl<$($name: Hash),*> Hash for ($($name,)*) where last_type!($($name,)+): ?Sized {
#[allow(non_snake_case)]
fn hash<S: Hasher>(&self, state: &mut S) {
let ($(ref $name,)*) = *self;
Expand All @@ -569,6 +569,11 @@ mod impls {
);
}

macro_rules! last_type {
($a:ident,) => { $a };
($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) };
}

impl_hash_tuple! {}
impl_hash_tuple! { A }
impl_hash_tuple! { A B }
Expand Down
14 changes: 10 additions & 4 deletions src/libcore/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ macro_rules! tuple_impls {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<$($T:PartialEq),+> PartialEq for ($($T,)+) {
impl<$($T:PartialEq),+> PartialEq for ($($T,)+) where last_type!($($T,)+): ?Sized {
#[inline]
fn eq(&self, other: &($($T,)+)) -> bool {
$(self.$idx == other.$idx)&&+
Expand All @@ -41,10 +41,11 @@ macro_rules! tuple_impls {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<$($T:Eq),+> Eq for ($($T,)+) {}
impl<$($T:Eq),+> Eq for ($($T,)+) where last_type!($($T,)+): ?Sized {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+) {
impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+)
where last_type!($($T,)+): ?Sized {
#[inline]
fn partial_cmp(&self, other: &($($T,)+)) -> Option<Ordering> {
lexical_partial_cmp!($(self.$idx, other.$idx),+)
Expand All @@ -68,7 +69,7 @@ macro_rules! tuple_impls {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<$($T:Ord),+> Ord for ($($T,)+) {
impl<$($T:Ord),+> Ord for ($($T,)+) where last_type!($($T,)+): ?Sized {
#[inline]
fn cmp(&self, other: &($($T,)+)) -> Ordering {
lexical_cmp!($(self.$idx, other.$idx),+)
Expand Down Expand Up @@ -118,6 +119,11 @@ macro_rules! lexical_cmp {
($a:expr, $b:expr) => { ($a).cmp(&$b) };
}

macro_rules! last_type {
($a:ident,) => { $a };
($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) };
}

tuple_impls! {
Tuple1 {
(0) -> A
Expand Down
29 changes: 29 additions & 0 deletions src/test/run-pass/unsized-tuple-impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(unsized_tuple_coercion)]

use std::collections::HashSet;

fn main() {
let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]);
let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]);
let mut a = [y, x];
a.sort();
assert_eq!(a, [x, y]);

assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]");

let mut h = HashSet::new();
h.insert(x);
h.insert(y);
assert!(h.contains(x));
assert!(h.contains(y));
}

0 comments on commit e5288c9

Please sign in to comment.