Oooh...I think I heard of that before. At least, the concept is familiar, but I didn't know the name for it -- haven't needed to use one, though I don't typically deal in caching, aside from in-memory caching for high performance applications, but every time I've needed that, it needed to be lossless and highly concurrent and parallel, so this wouldn't work. For that, I've always relied on flurry.
An LRU map is similar but it is "primary storage" not cache, but still with a fixed capacity so when you try to put more in it than it can hold it has to discard something, and it does that by discarding the least-recently used item.
Ah, thus enabling stack allocation. Have you looked at this?
https://lib.rs/crates/small-co...
Has both a hash and a btree variant, both heapless, of lru. Might be worth building a struct that has the map contained in a refcell, then define your helper functions around the refcell to make your code more ergonomic. Let those helper functions handle the unwraps (or if you want to micro-optimize, then unwrap_unchecked, though usually this isn't necessary -- I've only found one case of a tight loop where the compiler couldn't optimize out the checks, which was for a custom lz77 implementation for unpacking/repacking data from a PS2 game.) As for passing your state around, there's a great, ergonomic way to do that already:
https://crates.io/crates/state
One other comment about something you said, which you might find interesting: You said that in Rust, moves are cheap. This isn't really true.
Depends on how you build your program. I think if you use that heapless version of Box, all you'd be moving is the pointer and nothing else. At least, that's the way Box works on the heap -- just an owned pointer, and when moved, the heap memory doesn't move anywhere, just the pointer on the stack does. And that is cheap.
Also if you're not doing it, I highly recommend using 'cargo clippy'. Despite the name, it's actually far more useful than it is annoying.