use cached::macros::cached;
struct Worker {
id: u64,
factor: u32,
}
impl Worker {
fn new(id: u64, factor: u32) -> Self {
Self { id, factor }
}
#[cached(
in_impl = true,
key = "(u64, u32)",
convert = { (self.id, n) },
companions_vis = "pub(crate)"
)]
fn compute(&self, n: u32) -> u32 {
println!(" [miss] Worker(id={}) compute(n={n})", self.id);
self.factor * n
}
}
struct Config {
multiplier: u32,
base: u32,
}
impl Config {
fn new(multiplier: u32, base: u32) -> Self {
Self { multiplier, base }
}
fn compute(&self, n: u32) -> u32 {
config_compute(self.multiplier, self.base, n)
}
}
#[cached]
fn config_compute(multiplier: u32, base: u32, n: u32) -> u32 {
println!(" [miss] config_compute({multiplier}, {base}, {n})");
multiplier * base + n
}
trait Processor {
fn id(&self) -> u64;
fn factor(&self) -> u32;
fn process(&self, input: u32) -> u32 {
processor_compute(self.id(), self.factor(), input)
}
}
#[cached(key = "(u64, u32)", convert = { (id, input) })]
fn processor_compute(id: u64, factor: u32, input: u32) -> u32 {
println!(" [miss] processor_compute(id={id}, factor={factor}, input={input})");
input * factor
}
struct FastProcessor {
id: u64,
factor: u32,
}
impl Processor for FastProcessor {
fn id(&self) -> u64 {
self.id
}
fn factor(&self) -> u32 {
self.factor
}
}
pub fn main() {
println!("=== Part 1: in_impl = true with per-instance key ===");
let wa = Worker::new(1, 3);
let wb = Worker::new(2, 7);
println!("wa.compute(5), first call (expect miss):");
let r1 = wa.compute(5);
println!(" result = {r1}");
assert_eq!(r1, 3 * 5);
println!("wa.compute(5), second call (same id -> expect hit, no [miss]):");
let r2 = wa.compute(5);
println!(" result = {r2}");
assert_eq!(r1, r2);
println!("wb.compute(5), first call (different id -> expect miss):");
let r3 = wb.compute(5);
println!(" result = {r3}");
assert_eq!(r3, 7 * 5);
println!("wb.compute(5), second call (same id -> expect hit):");
let r4 = wb.compute(5);
assert_eq!(r3, r4);
println!("wa.compute_no_cache(5) bypasses cache (always recomputes):");
let r5 = wa.compute_no_cache(5);
println!(" result = {r5}");
assert_eq!(r5, 3 * 5);
println!("\n=== Part 2: free-function wrapper ===");
let cfg = Config::new(3, 10);
println!("First call (expect miss):");
let v1 = cfg.compute(5);
println!(" result = {v1}");
println!("Second call with same args (expect hit, no [miss] line):");
let v2 = cfg.compute(5);
println!(" result = {v2}");
assert_eq!(v1, v2);
println!("Call with different n=6 (expect miss):");
let v3 = cfg.compute(6);
println!(" result = {v3}");
assert_eq!(v3, 3 * 10 + 6);
println!("\n=== Part 3: dyn Trait keyed on stable id ===");
let p1: &dyn Processor = &FastProcessor { id: 1, factor: 7 };
let p2: &dyn Processor = &FastProcessor { id: 2, factor: 9 };
println!("p1, input=4, first call (expect miss):");
let r1 = p1.process(4);
println!(" result = {r1}");
println!("p1, input=4, second call (same id -> expect hit):");
let r2 = p1.process(4);
println!(" result = {r2}");
assert_eq!(r1, r2);
println!("p2, input=4, first call (different id -> expect miss):");
let r3 = p2.process(4);
println!(" result = {r3}");
assert_eq!(r3, 4 * 9);
println!("\ndone!");
}