From 777b15f3a5927ddc7a97f40a79c834d0ac49c629 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 20 Mar 2023 13:50:20 +0000 Subject: [PATCH 1/3] Drive-by cleanup: simplify documentation of `HashSet` ops --- library/std/src/collections/hash/set.rs | 44 +++++-------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index d611353b0d3f2..716d92239b930 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -1139,15 +1139,8 @@ where /// let a = HashSet::from([1, 2, 3]); /// let b = HashSet::from([3, 4, 5]); /// - /// let set = &a | &b; - /// - /// let mut i = 0; - /// let expected = [1, 2, 3, 4, 5]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); + /// let result = &a | &b; + /// assert_eq!(result, HashSet::from([1, 2, 3, 4, 5])); /// ``` fn bitor(self, rhs: &HashSet) -> HashSet { self.union(rhs).cloned().collect() @@ -1172,15 +1165,8 @@ where /// let a = HashSet::from([1, 2, 3]); /// let b = HashSet::from([2, 3, 4]); /// - /// let set = &a & &b; - /// - /// let mut i = 0; - /// let expected = [2, 3]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); + /// let result = &a & &b; + /// assert_eq!(result, HashSet::from([2, 3])); /// ``` fn bitand(self, rhs: &HashSet) -> HashSet { self.intersection(rhs).cloned().collect() @@ -1205,15 +1191,8 @@ where /// let a = HashSet::from([1, 2, 3]); /// let b = HashSet::from([3, 4, 5]); /// - /// let set = &a ^ &b; - /// - /// let mut i = 0; - /// let expected = [1, 2, 4, 5]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); + /// let result = &a ^ &b; + /// assert_eq!(result, HashSet::from([1, 2, 4, 5])); /// ``` fn bitxor(self, rhs: &HashSet) -> HashSet { self.symmetric_difference(rhs).cloned().collect() @@ -1238,15 +1217,8 @@ where /// let a = HashSet::from([1, 2, 3]); /// let b = HashSet::from([3, 4, 5]); /// - /// let set = &a - &b; - /// - /// let mut i = 0; - /// let expected = [1, 2]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); + /// let result = &a - &b; + /// assert_eq!(result, HashSet::from([1, 2])); /// ``` fn sub(self, rhs: &HashSet) -> HashSet { self.difference(rhs).cloned().collect() From a2df2226f98af57ed9e805368822eda16d46e07f Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 20 Mar 2023 13:34:16 +0000 Subject: [PATCH 2/3] Add implementations of `ops` for `HashSet` that keep ownership --- library/std/src/collections/hash/set.rs | 133 ++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 716d92239b930..a3b9188261c38 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -1147,6 +1147,38 @@ where } } +#[stable(feature = "set_owned_ops", since = "CURRENT_RUSTC_VERSION")] +impl BitOr> for HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + type Output = HashSet; + + /// Returns the union of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([3, 4, 5]); + /// + /// let result = a | b; + /// assert_eq!(result, HashSet::from([1, 2, 3, 4, 5])); + /// ``` + fn bitor(self, rhs: HashSet) -> HashSet { + // Try to avoid allocations by keeping set with the bigger capacity, + // try to avoid unnecessary moves, by keeping set with the bigger length + let [a, mut b] = minmax_by_key(self, rhs, |set| (set.capacity(), set.len())); + + b.extend(a); + + b + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl BitAnd<&HashSet> for &HashSet where @@ -1173,6 +1205,33 @@ where } } +#[stable(feature = "set_owned_ops", since = "CURRENT_RUSTC_VERSION")] +impl BitAnd<&HashSet> for HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + type Output = HashSet; + + /// Returns the intersection of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([2, 3, 4]); + /// + /// let result = a & &b; + /// assert_eq!(result, HashSet::from([2, 3])); + /// ``` + fn bitand(mut self, rhs: &HashSet) -> HashSet { + self.retain(|e| rhs.contains(e)); + self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl BitXor<&HashSet> for &HashSet where @@ -1199,6 +1258,41 @@ where } } +#[stable(feature = "set_owned_ops", since = "CURRENT_RUSTC_VERSION")] +impl BitXor> for HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + type Output = HashSet; + + /// Returns the symmetric difference of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([3, 4, 5]); + /// + /// let result = a ^ b; + /// assert_eq!(result, HashSet::from([1, 2, 4, 5])); + /// ``` + fn bitxor(self, rhs: HashSet) -> HashSet { + // Iterate through the smaller set + let [mut a, mut b] = minmax_by_key(self, rhs, HashSet::len); + + // This is essentially + // a = a - b (retain elements that are *not* in b) + // b = b - a (remove all elements that are in a) + a.retain(|e| !b.remove(e)); + + // Union of the differences + a | b + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Sub<&HashSet> for &HashSet where @@ -1225,6 +1319,41 @@ where } } +#[stable(feature = "set_owned_ops", since = "CURRENT_RUSTC_VERSION")] +impl Sub<&HashSet> for HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + type Output = HashSet; + + /// Returns the difference of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([3, 4, 5]); + /// + /// let result = a - &b; + /// assert_eq!(result, HashSet::from([1, 2])); + /// ``` + fn sub(mut self, rhs: &HashSet) -> HashSet { + // Iterate the smaller set, removing elements that are in `rhs` from `self` + if self.len() <= rhs.len() { + self.retain(|e| !rhs.contains(e)); + } else { + rhs.iter().for_each(|e| { + self.remove(e); + }) + } + + self + } +} + /// An iterator over the items of a `HashSet`. /// /// This `struct` is created by the [`iter`] method on [`HashSet`]. @@ -1885,3 +2014,7 @@ fn assert_covariance() { d } } + +fn minmax_by_key(a: T, b: T, k: impl Fn(&T) -> K) -> [T; 2] { + if k(&a) <= k(&b) { [a, b] } else { [b, a] } +} From 80b47a367247f91c469fcd1e7e44232f14504c79 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 20 Mar 2023 14:50:04 +0000 Subject: [PATCH 3/3] Add implementations of `ops` for `BTreeSet` that keep ownership --- library/alloc/src/collections/btree/set.rs | 116 +++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 973e7c660670c..98ff028c6f78f 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1545,6 +1545,37 @@ impl Sub<&BTreeSet> for &BTreeSet Sub<&BTreeSet> for BTreeSet { + type Output = BTreeSet; + + /// Returns the difference of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a = BTreeSet::from([1, 2, 3]); + /// let b = BTreeSet::from([3, 4, 5]); + /// + /// let result = a - &b; + /// assert_eq!(result, BTreeSet::from([1, 2])); + /// ``` + fn sub(mut self, rhs: &BTreeSet) -> BTreeSet { + // Iterate the smaller set, removing elements that are in `rhs` from `self` + if self.len() <= rhs.len() { + self.retain(|e| !rhs.contains(e)); + } else { + rhs.iter().for_each(|e| { + self.remove(e); + }) + } + + self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl BitXor<&BTreeSet> for &BTreeSet { type Output = BTreeSet; @@ -1570,6 +1601,37 @@ impl BitXor<&BTreeSet> for &BTreeSet } } +#[stable(feature = "set_owned_ops", since = "CURRENT_RUSTC_VERSION")] +impl BitXor> for BTreeSet { + type Output = BTreeSet; + + /// Returns the symmetric difference of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a = BTreeSet::from([1, 2, 3]); + /// let b = BTreeSet::from([2, 3, 4]); + /// + /// let result = a ^ b; + /// assert_eq!(result, BTreeSet::from([1, 4])); + /// ``` + fn bitxor(self, rhs: BTreeSet) -> BTreeSet { + // Iterate through the smaller set + let [mut a, mut b] = minmax_by_key(self, rhs, BTreeSet::len); + + // This is essentially + // a = a - b (retain elements that are *not* in b) + // b = b - a (remove all elements that are in a) + a.retain(|e| !b.remove(e)); + + // Union of the differences + a | b + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl BitAnd<&BTreeSet> for &BTreeSet { type Output = BTreeSet; @@ -1595,6 +1657,29 @@ impl BitAnd<&BTreeSet> for &BTreeSet } } +#[stable(feature = "set_owned_ops", since = "CURRENT_RUSTC_VERSION")] +impl BitAnd<&BTreeSet> for BTreeSet { + type Output = BTreeSet; + + /// Returns the intersection of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a = BTreeSet::from([1, 2, 3]); + /// let b = BTreeSet::from([2, 3, 4]); + /// + /// let result = a & &b; + /// assert_eq!(result, BTreeSet::from([2, 3])); + /// ``` + fn bitand(mut self, rhs: &BTreeSet) -> BTreeSet { + self.retain(|e| rhs.contains(e)); + self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl BitOr<&BTreeSet> for &BTreeSet { type Output = BTreeSet; @@ -1620,6 +1705,33 @@ impl BitOr<&BTreeSet> for &BTreeSet< } } +#[stable(feature = "set_owned_ops", since = "CURRENT_RUSTC_VERSION")] +impl BitOr> for BTreeSet { + type Output = BTreeSet; + + /// Returns the union of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a = BTreeSet::from([1, 2, 3]); + /// let b = BTreeSet::from([3, 4, 5]); + /// + /// let result = a | b; + /// assert_eq!(result, BTreeSet::from([1, 2, 3, 4, 5])); + /// ``` + fn bitor(self, rhs: BTreeSet) -> BTreeSet { + // Try to avoid unnecessary moves, by keeping set with the bigger length + let [a, mut b] = minmax_by_key(self, rhs, BTreeSet::len); + + b.extend(a); + + b + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Debug for BTreeSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -2397,5 +2509,9 @@ impl<'a, T: Ord, A: Allocator + Clone> CursorMutKey<'a, T, A> { #[unstable(feature = "btree_cursors", issue = "107540")] pub use super::map::UnorderedKeyError; +fn minmax_by_key(a: T, b: T, k: impl Fn(&T) -> K) -> [T; 2] { + if k(&a) <= k(&b) { [a, b] } else { [b, a] } +} + #[cfg(test)] mod tests;