Skip to content

Commit 16fbf98

Browse files
Auto merge of #145628 - tinnamchoi:fix-btree-append, r=<try>
[std][BTree] Fix behavior of `::append` to match documentation, `::insert`, and `::extend`
2 parents 160e762 + 002a09a commit 16fbf98

File tree

4 files changed

+72
-5
lines changed

4 files changed

+72
-5
lines changed

library/alloc/src/collections/btree/append.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,14 @@ where
104104
{
105105
type Item = (K, V);
106106

107-
/// If two keys are equal, returns the key-value pair from the right source.
107+
/// If two keys are equal, returns the key from the left and the value from the right.
108108
fn next(&mut self) -> Option<(K, V)> {
109109
let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0));
110-
b_next.or(a_next)
110+
match (a_next, b_next) {
111+
(Some((a_k, _)), Some((_, b_v))) => Some((a_k, b_v)),
112+
(Some(a), None) => Some(a),
113+
(None, Some(b)) => Some(b),
114+
(None, None) => None,
115+
}
111116
}
112117
}

library/alloc/src/collections/btree/map.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,10 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
11741174
///
11751175
/// If a key from `other` is already present in `self`, the respective
11761176
/// value from `self` will be overwritten with the respective value from `other`.
1177+
/// Similar to [`insert`], though, the key is not overwritten,
1178+
/// which matters for types that can be `==` without being identical.
1179+
///
1180+
/// [`insert`]: BTreeMap::insert
11771181
///
11781182
/// # Examples
11791183
///

library/alloc/src/collections/btree/map/tests.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::fmt::Debug;
1111
use crate::rc::Rc;
1212
use crate::string::{String, ToString};
1313
use crate::testing::crash_test::{CrashTestDummy, Panic};
14-
use crate::testing::ord_chaos::{Cyclic3, Governed, Governor};
14+
use crate::testing::ord_chaos::{Cyclic3, Governed, Governor, IdBased};
1515
use crate::testing::rng::DeterministicRng;
1616

1717
// Minimum number of elements to insert, to guarantee a tree with 2 levels,
@@ -2137,9 +2137,9 @@ fn test_append_drop_leak() {
21372137
let mut left = BTreeMap::new();
21382138
let mut right = BTreeMap::new();
21392139
left.insert(a.spawn(Panic::Never), ());
2140-
left.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append
2140+
left.insert(b.spawn(Panic::Never), ());
21412141
left.insert(c.spawn(Panic::Never), ());
2142-
right.insert(b.spawn(Panic::Never), ());
2142+
right.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append
21432143
right.insert(c.spawn(Panic::Never), ());
21442144

21452145
catch_unwind(move || left.append(&mut right)).unwrap_err();
@@ -2587,3 +2587,31 @@ fn cursor_peek_prev_agrees_with_cursor_mut() {
25872587
let prev = cursor.peek_prev();
25882588
assert_matches!(prev, Some((&3, _)));
25892589
}
2590+
2591+
#[test]
2592+
fn test_id_based_insert() {
2593+
let mut lhs = BTreeMap::new();
2594+
let mut rhs = BTreeMap::new();
2595+
2596+
lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "lhs_v".to_string());
2597+
rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "rhs_v".to_string());
2598+
2599+
for (k, v) in rhs.into_iter() {
2600+
lhs.insert(k, v);
2601+
}
2602+
2603+
assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string());
2604+
}
2605+
2606+
#[test]
2607+
fn test_id_based_append() {
2608+
let mut lhs = BTreeMap::new();
2609+
let mut rhs = BTreeMap::new();
2610+
2611+
lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "lhs_v".to_string());
2612+
rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "rhs_v".to_string());
2613+
2614+
lhs.append(&mut rhs);
2615+
2616+
assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string());
2617+
}

library/alloctests/testing/ord_chaos.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::cell::Cell;
22
use std::cmp::Ordering::{self, *};
33
use std::ptr;
44

5+
use crate::string::String;
6+
57
// Minimal type with an `Ord` implementation violating transitivity.
68
#[derive(Debug)]
79
pub(crate) enum Cyclic3 {
@@ -79,3 +81,31 @@ impl<T: PartialEq> PartialEq for Governed<'_, T> {
7981
}
8082

8183
impl<T: Eq> Eq for Governed<'_, T> {}
84+
85+
// Comparison based only on the ID, the name is ignored.
86+
#[derive(Debug)]
87+
pub(crate) struct IdBased {
88+
pub id: u32,
89+
#[allow(dead_code)]
90+
pub name: String,
91+
}
92+
93+
impl PartialEq for IdBased {
94+
fn eq(&self, other: &Self) -> bool {
95+
self.id == other.id
96+
}
97+
}
98+
99+
impl Eq for IdBased {}
100+
101+
impl PartialOrd for IdBased {
102+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
103+
Some(self.cmp(other))
104+
}
105+
}
106+
107+
impl Ord for IdBased {
108+
fn cmp(&self, other: &Self) -> Ordering {
109+
self.id.cmp(&other.id)
110+
}
111+
}

0 commit comments

Comments
 (0)