about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRobert Bamler <robamler@users.noreply.github.com>2019-11-19 19:57:03 -0800
committerRobert Bamler <robert.bamler@gmail.com>2019-11-21 23:16:44 -0800
commit5028fd8ab9bda648840bb48e03e618f027cc8c85 (patch)
treeb6ff1ab958c2c6582edf1ee4348a4bd50ae0e5e8
parent618b01f9fa0a6b4e7e2ce5b3409abe104b80c4a8 (diff)
downloadrust-5028fd8ab9bda648840bb48e03e618f027cc8c85.tar.gz
rust-5028fd8ab9bda648840bb48e03e618f027cc8c85.zip
Document pitfall with `impl PartialEq<B> for A`
Fixes #66476 by turning the violating example into an explicit
counterexample.
-rw-r--r--src/libcore/cmp.rs32
1 files changed, 18 insertions, 14 deletions
diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs
index 1ac51291b93..1fb3e89a42f 100644
--- a/src/libcore/cmp.rs
+++ b/src/libcore/cmp.rs
@@ -135,10 +135,15 @@ use self::Ordering::*;
 /// By changing `impl PartialEq for Book` to `impl PartialEq<BookFormat> for Book`,
 /// we allow `BookFormat`s to be compared with `Book`s.
 ///
-/// You can also combine these implementations to let the `==` operator work with
-/// two different types:
-///
-/// ```
+/// A comparison like the one above, which ignores some fields of the struct,
+/// can be dangerous. It can easily lead to an unintended violation of the
+/// requirements for a partial equivalence relation. For example, if we kept
+/// the above implementation of `PartialEq<Book>` for `BookFormat` and added an
+/// implementation of `PartialEq<Book>` for `Book` (either via a `#[derive]` or
+/// via the manual implementation from the first example) then the result would
+/// violate transitivity:
+///
+/// ```should_panic
 /// #[derive(PartialEq)]
 /// enum BookFormat {
 ///     Paperback,
@@ -146,6 +151,7 @@ use self::Ordering::*;
 ///     Ebook,
 /// }
 ///
+/// #[derive(PartialEq)]
 /// struct Book {
 ///     isbn: i32,
 ///     format: BookFormat,
@@ -163,18 +169,16 @@ use self::Ordering::*;
 ///     }
 /// }
 ///
-/// impl PartialEq for Book {
-///     fn eq(&self, other: &Book) -> bool {
-///         self.isbn == other.isbn
-///     }
-/// }
+/// fn main() {
+///     let b1 = Book { isbn: 1, format: BookFormat::Paperback };
+///     let b2 = Book { isbn: 2, format: BookFormat::Paperback };
 ///
-/// let b1 = Book { isbn: 3, format: BookFormat::Paperback };
-/// let b2 = Book { isbn: 3, format: BookFormat::Ebook };
+///     assert!(b1 == BookFormat::Paperback);
+///     assert!(BookFormat::Paperback == b2);
 ///
-/// assert!(b1 == BookFormat::Paperback);
-/// assert!(BookFormat::Ebook != b1);
-/// assert!(b1 == b2);
+///     // The following should hold by transitivity but doesn't.
+///     assert!(b1 == b2); // <-- PANICS
+/// }
 /// ```
 ///
 /// # Examples