Skip to content

Commit e313628

Browse files
liwenjieQuWenjie Li
andauthored
feat: Add equivalent trait support for flexible key lookups (#74)
--------- Co-authored-by: Wenjie Li <[email protected]>
1 parent d2cf016 commit e313628

File tree

5 files changed

+302
-45
lines changed

5 files changed

+302
-45
lines changed

src/double_priority_queue/mod.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ use crate::core_iterators::*;
3939
use crate::store::{left, level, parent, right};
4040
use crate::store::{Index, Position, Store};
4141
use crate::TryReserveError;
42+
use equivalent::Equivalent;
4243
use iterators::*;
4344

44-
use std::borrow::Borrow;
4545
use std::cmp::{Eq, Ord};
4646
#[cfg(feature = "std")]
4747
use std::collections::hash_map::RandomState;
@@ -722,8 +722,7 @@ where
722722
/// The operation is performed in **O(log(N))** time.
723723
pub fn change_priority<Q>(&mut self, item: &Q, new_priority: P) -> Option<P>
724724
where
725-
I: Borrow<Q>,
726-
Q: Eq + Hash + ?Sized,
725+
Q: ?Sized + Equivalent<I> + Hash,
727726
{
728727
self.store
729728
.change_priority(item, new_priority)
@@ -743,8 +742,7 @@ where
743742
/// The operation is performed in **O(log(N))** time (worst case).
744743
pub fn change_priority_by<Q, F>(&mut self, item: &Q, priority_setter: F) -> bool
745744
where
746-
I: Borrow<Q>,
747-
Q: Eq + Hash + ?Sized,
745+
Q: ?Sized + Equivalent<I> + Hash,
748746
F: FnOnce(&mut P),
749747
{
750748
self.store
@@ -758,8 +756,7 @@ where
758756
/// Get the priority of an item, or `None`, if the item is not in the queue
759757
pub fn get_priority<Q>(&self, item: &Q) -> Option<&P>
760758
where
761-
I: Borrow<Q>,
762-
Q: Eq + Hash + ?Sized,
759+
Q: ?Sized + Equivalent<I> + Hash,
763760
{
764761
self.store.get_priority(item)
765762
}
@@ -769,8 +766,7 @@ where
769766
/// Returns `true` if `item` is in the queue, `false` if it is not.
770767
pub fn contains<Q>(&self, item: &Q) -> bool
771768
where
772-
I: Borrow<Q>,
773-
Q: Eq + Hash + ?Sized,
769+
Q: ?Sized + Equivalent<I> + Hash,
774770
{
775771
self.store.contains(item)
776772
}
@@ -779,8 +775,7 @@ where
779775
/// or `None` if the item is not in the queue.
780776
pub fn get<Q>(&self, item: &Q) -> Option<(&I, &P)>
781777
where
782-
I: Borrow<Q>,
783-
Q: Eq + Hash + ?Sized,
778+
Q: ?Sized + Equivalent<I> + Hash,
784779
{
785780
self.store.get(item)
786781
}
@@ -797,8 +792,7 @@ where
797792
/// [`change_priority_by`](DoublePriorityQueue::change_priority_by).
798793
pub fn get_mut<Q>(&mut self, item: &Q) -> Option<(&mut I, &P)>
799794
where
800-
I: Borrow<Q>,
801-
Q: Eq + Hash + ?Sized,
795+
Q: ?Sized + Equivalent<I> + Hash,
802796
{
803797
self.store.get_mut(item)
804798
}
@@ -810,8 +804,7 @@ where
810804
/// The operation is performed in **O(log(N))** time (worst case).
811805
pub fn remove<Q>(&mut self, item: &Q) -> Option<(I, P)>
812806
where
813-
I: Borrow<Q>,
814-
Q: Eq + Hash + ?Sized,
807+
Q: ?Sized + Equivalent<I> + Hash,
815808
{
816809
self.store.remove(item).map(|(item, priority, pos)| {
817810
if pos.0 < self.len() {

src/priority_queue/mod.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ use crate::core_iterators::*;
4040
use crate::store::{left, parent, right};
4141
use crate::store::{Index, Position, Store};
4242
use crate::TryReserveError;
43+
use equivalent::Equivalent;
4344
use iterators::*;
4445

45-
use std::borrow::Borrow;
4646
use std::cmp::{Eq, Ord};
4747
#[cfg(feature = "std")]
4848
use std::collections::hash_map::RandomState;
@@ -596,8 +596,7 @@ where
596596
/// The operation is performed in **O(log(N))** time.
597597
pub fn change_priority<Q>(&mut self, item: &Q, new_priority: P) -> Option<P>
598598
where
599-
I: Borrow<Q>,
600-
Q: Eq + Hash + ?Sized,
599+
Q: ?Sized + Equivalent<I> + Hash,
601600
{
602601
self.store
603602
.change_priority(item, new_priority)
@@ -618,8 +617,7 @@ where
618617
/// The operation is performed in **O(log(N))** time (worst case).
619618
pub fn change_priority_by<Q, F>(&mut self, item: &Q, priority_setter: F) -> bool
620619
where
621-
I: Borrow<Q>,
622-
Q: Eq + Hash + ?Sized,
620+
Q: ?Sized + Equivalent<I> + Hash,
623621
F: FnOnce(&mut P),
624622
{
625623
self.store
@@ -633,8 +631,7 @@ where
633631
/// Get the priority of an item, or `None`, if the item is not in the queue
634632
pub fn get_priority<Q>(&self, item: &Q) -> Option<&P>
635633
where
636-
I: Borrow<Q>,
637-
Q: Eq + Hash + ?Sized,
634+
Q: ?Sized + Equivalent<I> + Hash,
638635
{
639636
self.store.get_priority(item)
640637
}
@@ -644,8 +641,7 @@ where
644641
/// Returns `true` if `item` is in the queue, `false` if it is not.
645642
pub fn contains<Q>(&self, item: &Q) -> bool
646643
where
647-
I: Borrow<Q>,
648-
Q: Eq + Hash + ?Sized,
644+
Q: ?Sized + Equivalent<I> + Hash,
649645
{
650646
self.store.contains(item)
651647
}
@@ -654,8 +650,7 @@ where
654650
/// or `None` if the item is not in the queue.
655651
pub fn get<Q>(&self, item: &Q) -> Option<(&I, &P)>
656652
where
657-
I: Borrow<Q>,
658-
Q: Eq + Hash + ?Sized,
653+
Q: ?Sized + Equivalent<I> + Hash,
659654
{
660655
self.store.get(item)
661656
}
@@ -672,8 +667,7 @@ where
672667
/// [`change_priority_by`](PriorityQueue::change_priority_by).
673668
pub fn get_mut<Q>(&mut self, item: &Q) -> Option<(&mut I, &P)>
674669
where
675-
I: Borrow<Q>,
676-
Q: Eq + Hash + ?Sized,
670+
Q: ?Sized + Equivalent<I> + Hash,
677671
{
678672
self.store.get_mut(item)
679673
}
@@ -709,8 +703,7 @@ where
709703
/// The operation is performed in **O(log(N))** time (worst case).
710704
pub fn remove<Q>(&mut self, item: &Q) -> Option<(I, P)>
711705
where
712-
I: Borrow<Q>,
713-
Q: Eq + Hash + ?Sized,
706+
Q: ?Sized + Equivalent<I> + Hash,
714707
{
715708
self.store.remove(item).map(|(item, priority, pos)| {
716709
if pos.0 < self.len() {

src/store.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use std::vec::Vec;
3232
// as vec instead of the IndexMap
3333
use crate::core_iterators::*;
3434
use crate::TryReserveError;
35+
use equivalent::Equivalent;
3536

36-
use std::borrow::Borrow;
3737
use std::cmp::{Eq, Ord};
3838
#[cfg(feature = "std")]
3939
use std::collections::hash_map::RandomState;
@@ -425,8 +425,7 @@ where
425425
/// The operation is performed in **O(log(N))** time.
426426
pub fn change_priority<Q>(&mut self, item: &Q, mut new_priority: P) -> Option<(P, Position)>
427427
where
428-
I: Borrow<Q>,
429-
Q: Eq + Hash + ?Sized,
428+
Q: ?Sized + Equivalent<I> + Hash,
430429
{
431430
let Store { map, qp, .. } = self;
432431
map.get_full_mut(item).map(|(index, _, p)| {
@@ -441,8 +440,7 @@ where
441440
/// The operation is performed in **O(log(N))** time (worst case).
442441
pub fn change_priority_by<Q, F>(&mut self, item: &Q, priority_setter: F) -> Option<Position>
443442
where
444-
I: Borrow<Q>,
445-
Q: Eq + Hash + ?Sized,
443+
Q: ?Sized + Equivalent<I> + Hash,
446444
F: FnOnce(&mut P),
447445
{
448446
let Store { map, qp, .. } = self;
@@ -455,8 +453,7 @@ where
455453
/// Get the priority of an item, or `None`, if the item is not in the queue
456454
pub fn get_priority<Q>(&self, item: &Q) -> Option<&P>
457455
where
458-
I: Borrow<Q>,
459-
Q: Eq + Hash + ?Sized,
456+
Q: ?Sized + Equivalent<I> + Hash,
460457
{
461458
self.map.get(item)
462459
}
@@ -466,8 +463,7 @@ where
466463
/// Returns `true` if `item` is in the store, `false` if it is not.
467464
pub fn contains<Q>(&self, item: &Q) -> bool
468465
where
469-
I: Borrow<Q>,
470-
Q: Eq + Hash + ?Sized,
466+
Q: ?Sized + Equivalent<I> + Hash,
471467
{
472468
self.map.contains_key(item)
473469
}
@@ -476,8 +472,7 @@ where
476472
/// or `None` if the item is not in the queue.
477473
pub fn get<Q>(&self, item: &Q) -> Option<(&I, &P)>
478474
where
479-
I: Borrow<Q>,
480-
Q: Eq + Hash + ?Sized,
475+
Q: ?Sized + Equivalent<I> + Hash,
481476
{
482477
self.map.get_full(item).map(|(_, k, v)| (k, v))
483478
}
@@ -493,16 +488,14 @@ where
493488
/// `change_priority_by`.
494489
pub fn get_mut<Q>(&mut self, item: &Q) -> Option<(&mut I, &P)>
495490
where
496-
I: Borrow<Q>,
497-
Q: Eq + Hash + ?Sized,
491+
Q: ?Sized + Equivalent<I> + Hash,
498492
{
499493
self.map.get_full_mut2(item).map(|(_, k, v)| (k, &*v))
500494
}
501495

502496
pub fn remove<Q>(&mut self, item: &Q) -> Option<(I, P, Position)>
503497
where
504-
I: Borrow<Q>,
505-
Q: Eq + Hash + ?Sized,
498+
Q: ?Sized + Equivalent<I> + Hash,
506499
{
507500
self.map.swap_remove_full(item).map(|(i, item, priority)| {
508501
let i = Index(i);

tests/double_priority_queue.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,145 @@ mod doublepq_tests {
10571057
assert_eq!(dpq.pop_min(), Some(('c', 1)));
10581058
}
10591059

1060+
#[test]
1061+
fn equivalent_blanket_implementation() {
1062+
let mut dpq = DoublePriorityQueue::new();
1063+
1064+
// Push with owned String
1065+
dpq.push("Alice".to_string(), 10);
1066+
dpq.push("Bob".to_string(), 20);
1067+
1068+
// Test get_priority with &str (uses String's blanket Equivalent impl)
1069+
assert_eq!(dpq.get_priority("Alice"), Some(&10));
1070+
assert_eq!(dpq.get_priority("Bob"), Some(&20));
1071+
assert_eq!(dpq.get_priority("Charlie"), None);
1072+
1073+
// Test contains with &str
1074+
assert!(dpq.contains("Alice"));
1075+
assert!(dpq.contains("Bob"));
1076+
assert!(!dpq.contains("Charlie"));
1077+
1078+
// Test change_priority with &str
1079+
assert_eq!(dpq.change_priority("Alice", 25), Some(10));
1080+
assert_eq!(dpq.get_priority("Alice"), Some(&25));
1081+
1082+
// Test change_priority_by with &str
1083+
assert!(dpq.change_priority_by("Alice", |p| *p += 5));
1084+
assert_eq!(dpq.get_priority("Alice"), Some(&30));
1085+
1086+
// Test get with &str
1087+
let (item, priority) = dpq.get("Bob").unwrap();
1088+
assert_eq!(item, "Bob");
1089+
assert_eq!(*priority, 20);
1090+
1091+
// Test get_mut with &str
1092+
let (item_mut, priority) = dpq.get_mut("Bob").unwrap();
1093+
assert_eq!(item_mut, "Bob");
1094+
assert_eq!(*priority, 20);
1095+
1096+
// Test remove with &str
1097+
assert_eq!(dpq.remove("Bob"), Some(("Bob".to_string(), 20)));
1098+
assert!(!dpq.contains("Bob"));
1099+
}
1100+
1101+
#[test]
1102+
fn equivalent_custom_implementation() {
1103+
use equivalent::Equivalent;
1104+
use std::hash::Hash;
1105+
1106+
#[derive(Debug, PartialEq, Eq, Hash)]
1107+
struct Person {
1108+
id: u32,
1109+
name: String,
1110+
age: u16,
1111+
}
1112+
1113+
#[derive(Debug, PartialEq, Eq, Hash)]
1114+
struct PersonView<'a> {
1115+
id: u32,
1116+
name: &'a str,
1117+
age: u16,
1118+
}
1119+
1120+
impl<'a> Equivalent<Person> for PersonView<'a> {
1121+
fn equivalent(&self, key: &Person) -> bool {
1122+
self.id == key.id && self.name == key.name && self.age == key.age
1123+
}
1124+
}
1125+
1126+
let mut dpq = DoublePriorityQueue::new();
1127+
1128+
// Create test persons
1129+
let alice = Person {
1130+
id: 1,
1131+
name: "Alice".to_string(),
1132+
age: 30,
1133+
};
1134+
let bob = Person {
1135+
id: 2,
1136+
name: "Bob".to_string(),
1137+
age: 25,
1138+
};
1139+
1140+
// Push persons into queue
1141+
dpq.push(alice, 100);
1142+
dpq.push(bob, 200);
1143+
1144+
// Create PersonView instances for querying
1145+
let alice_view = PersonView {
1146+
id: 1,
1147+
name: "Alice",
1148+
age: 30,
1149+
};
1150+
let bob_view = PersonView {
1151+
id: 2,
1152+
name: "Bob",
1153+
age: 25,
1154+
};
1155+
let charlie_view = PersonView {
1156+
id: 3,
1157+
name: "Charlie",
1158+
age: 35,
1159+
};
1160+
1161+
// Test get_priority with PersonView
1162+
assert_eq!(dpq.get_priority(&alice_view), Some(&100));
1163+
assert_eq!(dpq.get_priority(&bob_view), Some(&200));
1164+
assert_eq!(dpq.get_priority(&charlie_view), None);
1165+
1166+
// Test contains with PersonView
1167+
assert!(dpq.contains(&alice_view));
1168+
assert!(dpq.contains(&bob_view));
1169+
assert!(!dpq.contains(&charlie_view));
1170+
1171+
// Test change_priority with PersonView
1172+
assert_eq!(dpq.change_priority(&alice_view, 300), Some(100));
1173+
assert_eq!(dpq.get_priority(&alice_view), Some(&300));
1174+
1175+
// Test change_priority_by with PersonView
1176+
assert!(dpq.change_priority_by(&alice_view, |p| *p += 50));
1177+
assert_eq!(dpq.get_priority(&alice_view), Some(&350));
1178+
1179+
// Test get with PersonView
1180+
let (person, priority) = dpq.get(&bob_view).unwrap();
1181+
assert_eq!(person.name, "Bob");
1182+
assert_eq!(*priority, 200);
1183+
1184+
// Test get_mut with PersonView
1185+
let (person_mut, priority) = dpq.get_mut(&bob_view).unwrap();
1186+
assert_eq!(person_mut.name, "Bob");
1187+
assert_eq!(*priority, 200);
1188+
1189+
// Test remove with PersonView
1190+
let removed = dpq.remove(&bob_view);
1191+
assert!(removed.is_some());
1192+
let (removed_person, removed_priority) = removed.unwrap();
1193+
assert_eq!(removed_person.id, 2);
1194+
assert_eq!(removed_person.name, "Bob");
1195+
assert_eq!(removed_priority, 200);
1196+
assert!(!dpq.contains(&bob_view));
1197+
}
1198+
10601199
#[test]
10611200
fn user_test() {
10621201
use priority_queue::PriorityQueue;

0 commit comments

Comments
 (0)