Skip to content

Commit 2c314f9

Browse files
authored
Rollup merge of rust-lang#144330 - gewitternacht:document-clone-eq, r=Amanieu
document assumptions about `Clone` and `Eq` traits Most standard library collections break if `Clone` has a non-standard implementation which violates `x.clone() == x`. [Here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=b7fc6dfa8410cbb673eb8d38393d81de) the resulting broken behaviour of different collections is shown. I originally created an issue at rust-lang/hashbrown#629, but the conclusion there was that `x.clone()` resulting in an object that compares equal to the original one is probably a very universal assumption. However, this assumption is (to my knowledge) not documented anywhere. I propose to make this assumption explicit in the `Clone` trait documentation. The property that seems the most reasonable to me is the following: When implementing both `Clone` and `PartialEq`, then ```text x == x -> x.clone() == x ``` is expected to hold. This way, when also implementing `Eq`, it automatically follows that `x.clone() == x` has to hold, which should be enough for the collections to not break. At the same time, the property also works for the "normal" elements of a type with `PartialEq`. For the wording, I tried to follow the [`Hash` and `Eq`](https://doc.rust-lang.org/std/hash/trait.Hash.html#hash-and-eq) documentation. As I am fairly new to Rust, it might well be that this property cannot be generally expected – it seems reasonable to me, but any counter-examples or critique, both content- and wording-wise, would be very welcome. If the property turns out to be too general, I would suggest to at least document the assumption of `x.clone() == x` for the collections somehow. An additional thought of mine: If it is indeed generally expected that `x == x -> x.clone() == x`, then, for the sake of completeness, one could also define that `x != x -> y != y for y = x.clone()` should hold, i.e., that an object that did not compare equal to itself before cloning, should also not compare equal to itself afterwards.
2 parents d06b3b4 + fadc961 commit 2c314f9

File tree

1 file changed

+33
-0
lines changed

1 file changed

+33
-0
lines changed

library/core/src/clone.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,34 @@ mod uninit;
139139
/// // Note: With the manual implementations the above line will compile.
140140
/// ```
141141
///
142+
/// ## `Clone` and `PartialEq`/`Eq`
143+
/// `Clone` is intended for the duplication of objects. Consequently, when implementing
144+
/// both `Clone` and [`PartialEq`], the following property is expected to hold:
145+
/// ```text
146+
/// x == x -> x.clone() == x
147+
/// ```
148+
/// In other words, if an object compares equal to itself,
149+
/// its clone must also compare equal to the original.
150+
///
151+
/// For types that also implement [`Eq`] – for which `x == x` always holds –
152+
/// this implies that `x.clone() == x` must always be true.
153+
/// Standard library collections such as
154+
/// [`HashMap`], [`HashSet`], [`BTreeMap`], [`BTreeSet`] and [`BinaryHeap`]
155+
/// rely on their keys respecting this property for correct behavior.
156+
/// Furthermore, these collections require that cloning a key preserves the outcome of the
157+
/// [`Hash`] and [`Ord`] methods. Thankfully, this follows automatically from `x.clone() == x`
158+
/// if `Hash` and `Ord` are correctly implemented according to their own requirements.
159+
///
160+
/// When deriving both `Clone` and [`PartialEq`] using `#[derive(Clone, PartialEq)]`
161+
/// or when additionally deriving [`Eq`] using `#[derive(Clone, PartialEq, Eq)]`,
162+
/// then this property is automatically upheld – provided that it is satisfied by
163+
/// the underlying types.
164+
///
165+
/// Violating this property is a logic error. The behavior resulting from a logic error is not
166+
/// specified, but users of the trait must ensure that such logic errors do *not* result in
167+
/// undefined behavior. This means that `unsafe` code **must not** rely on this property
168+
/// being satisfied.
169+
///
142170
/// ## Additional implementors
143171
///
144172
/// In addition to the [implementors listed below][impls],
@@ -152,6 +180,11 @@ mod uninit;
152180
/// (even if the referent doesn't),
153181
/// while variables captured by mutable reference never implement `Clone`.
154182
///
183+
/// [`HashMap`]: ../../std/collections/struct.HashMap.html
184+
/// [`HashSet`]: ../../std/collections/struct.HashSet.html
185+
/// [`BTreeMap`]: ../../std/collections/struct.BTreeMap.html
186+
/// [`BTreeSet`]: ../../std/collections/struct.BTreeSet.html
187+
/// [`BinaryHeap`]: ../../std/collections/struct.BinaryHeap.html
155188
/// [impls]: #implementors
156189
#[stable(feature = "rust1", since = "1.0.0")]
157190
#[lang = "clone"]

0 commit comments

Comments
 (0)