Skip to content

Commit 4840227

Browse files
authored
Merge pull request yegor256#250 from owtotwo/master
Use the LRU Cache implementation as our usage example. (It fits our design well)
2 parents 60ba03d + 9d6f024 commit 4840227

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

.github/workflows/examples.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2023-2025 Yegor Bugayenko
2+
# SPDX-FileCopyrightText: Copyright (c) 2025 owtotwo
3+
# SPDX-License-Identifier: MIT
4+
---
5+
# yamllint disable rule:line-length
6+
name: examples
7+
'on':
8+
push:
9+
pull_request:
10+
jobs:
11+
run-examples:
12+
timeout-minutes: 15
13+
runs-on: ubuntu-24.04
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions-rs/toolchain@v1
17+
with:
18+
toolchain: stable
19+
- id: list-examples
20+
# For now we only identify single .rs file examples, and will not consider the examples
21+
# listed in cargo.toml or complex examples in the form of Cargo workspace folders.
22+
run: |
23+
{
24+
echo "examples<<EOF"
25+
find examples -name '*.rs' -print0 | xargs -0 -I {} basename {} .rs
26+
echo "EOF"
27+
} >> "$GITHUB_ENV"
28+
- run: |
29+
echo "${{ env.examples }}" | while IFS= read -r example; do
30+
cargo run --example "$example"
31+
done

examples/lru_cache.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2023-2025 Yegor Bugayenko
2+
// SPDX-FileCopyrightText: Copyright (c) 2025 owtotwo
3+
// SPDX-License-Identifier: MIT
4+
5+
/// Use micromap instead of HashMap in std to implement the classic data structure
6+
/// of LRU Cache as usage sample.
7+
/// (Ref: https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU)
8+
use micromap::Map as MicroMap;
9+
use std::collections::{HashMap, VecDeque};
10+
use std::hash::Hash;
11+
12+
mod lru_cache {
13+
use super::*;
14+
use map_in_lru::*;
15+
16+
#[derive(Debug, Clone)]
17+
pub struct LRUCache<K, V, const N: usize, M: Map<K, V> = MicroMap<K, V, N>> {
18+
capacity: usize,
19+
map: M,
20+
order: VecDeque<K>,
21+
_marker: std::marker::PhantomData<V>,
22+
}
23+
24+
// LRUCache core API
25+
impl<K: Hash + Eq + Clone, V, const N: usize, M: Map<K, V> + WithCapacity> LRUCache<K, V, N, M> {
26+
pub fn new() -> Self {
27+
LRUCache {
28+
capacity: const { N },
29+
map: M::with_capacity(N),
30+
order: VecDeque::new(),
31+
_marker: std::marker::PhantomData,
32+
}
33+
}
34+
35+
pub fn get(&mut self, key: &K) -> Option<&V> {
36+
if self.map.contains_key(key) {
37+
self.order.retain(|k| k != key);
38+
self.order.push_back(key.clone());
39+
self.map.get(key)
40+
} else {
41+
None
42+
}
43+
}
44+
45+
pub fn put(&mut self, key: K, value: V) {
46+
if self.map.contains_key(&key) {
47+
self.order.retain(|k| k != &key);
48+
} else if self.map.len() == self.capacity {
49+
if let Some(old_key) = self.order.pop_front() {
50+
self.map.remove(&old_key);
51+
}
52+
}
53+
self.order.push_back(key.clone());
54+
self.map.insert(key, value);
55+
}
56+
}
57+
}
58+
59+
mod map_in_lru {
60+
use super::*;
61+
62+
// Map in LRUCache has two trait requirements
63+
64+
pub trait Map<K, V> {
65+
fn contains_key(&self, key: &K) -> bool;
66+
fn get(&self, key: &K) -> Option<&V>;
67+
fn insert(&mut self, key: K, value: V);
68+
fn remove(&mut self, key: &K) -> Option<V>;
69+
fn len(&self) -> usize;
70+
}
71+
72+
pub trait WithCapacity {
73+
fn with_capacity(capacity: usize) -> Self;
74+
}
75+
76+
// Implementations for HashMap
77+
78+
impl<K: Eq + Hash, V> Map<K, V> for HashMap<K, V> {
79+
fn contains_key(&self, key: &K) -> bool {
80+
HashMap::contains_key(self, key)
81+
}
82+
83+
fn get(&self, key: &K) -> Option<&V> {
84+
HashMap::get(self, key)
85+
}
86+
87+
fn insert(&mut self, key: K, value: V) {
88+
HashMap::insert(self, key, value);
89+
}
90+
91+
fn remove(&mut self, key: &K) -> Option<V> {
92+
HashMap::remove(self, key)
93+
}
94+
95+
fn len(&self) -> usize {
96+
HashMap::len(self)
97+
}
98+
}
99+
100+
impl<K, V> WithCapacity for HashMap<K, V> {
101+
fn with_capacity(capacity: usize) -> Self {
102+
HashMap::with_capacity(capacity)
103+
}
104+
}
105+
106+
// Implementations for MicroMap
107+
108+
impl<K: PartialEq, V, const N: usize> Map<K, V> for MicroMap<K, V, N> {
109+
fn contains_key(&self, key: &K) -> bool {
110+
MicroMap::contains_key(self, key)
111+
}
112+
113+
fn get(&self, key: &K) -> Option<&V> {
114+
MicroMap::get(self, key)
115+
}
116+
117+
fn insert(&mut self, key: K, value: V) {
118+
MicroMap::insert(self, key, value);
119+
}
120+
121+
fn remove(&mut self, key: &K) -> Option<V> {
122+
MicroMap::remove(self, key)
123+
}
124+
125+
fn len(&self) -> usize {
126+
MicroMap::len(self)
127+
}
128+
}
129+
130+
impl<K: PartialEq, V, const N: usize> WithCapacity for MicroMap<K, V, N> {
131+
fn with_capacity(_capacity: usize) -> Self {
132+
MicroMap::<K, V, N>::new()
133+
}
134+
}
135+
}
136+
137+
fn main() {
138+
use lru_cache::LRUCache;
139+
140+
const MAX: usize = 2; // LRU Cache Capacity
141+
142+
{
143+
// micromap::Map
144+
let mut cache: LRUCache<_, _, MAX> = LRUCache::new(); //use MicroMap by default
145+
cache.put(1, 1);
146+
cache.put(2, 2);
147+
println!("{:?}", cache.get(&1)); // Some(&1)
148+
cache.put(3, 3);
149+
println!("{:?}", cache.get(&2)); // None (removed)
150+
cache.put(4, 4);
151+
println!("{:?}", cache.get(&1)); // None (removed)
152+
println!("{:?}", cache.get(&3)); // Some(&3)
153+
println!("{:?}", cache.get(&4)); // Some(&4)
154+
}
155+
156+
{
157+
// std::collection::HashMap
158+
let mut cache: LRUCache<_, _, MAX, HashMap<_, _>> = LRUCache::new();
159+
cache.put(1, 1);
160+
cache.put(2, 2);
161+
assert_eq!(cache.get(&1), Some(&1)); // Some(&1)
162+
cache.put(3, 3);
163+
assert_eq!(cache.get(&2), None); // None (removed)
164+
cache.put(4, 4);
165+
assert_eq!(cache.get(&1), None); // None (removed)
166+
assert_eq!(cache.get(&3), Some(&3)); // Some(&3)
167+
assert_eq!(cache.get(&4), Some(&4)); // Some(&4)
168+
}
169+
}

0 commit comments

Comments
 (0)