LRU Cache, Const Generics, and Embedded Stuff
Topics:
- Building an LRU Cache via @mysteriouspants
- Const Generics via @DanielPBank
- Float Stuff Also Const Generics via @PeterKehl
- Debugging on AMG8833 Grid-EYE with Rust via @jacobrosenthal
- VSCode Rust-analyzer Tips via @craig_jbishop
Building an LRU Cache via @mysteriouspants
Repo: https://github.com/mysteriouspants/mysterious_cache
Chris made a Least Recently Used (LRU) Cache: a hash map of fixed size that we can add nodes to until we reach its limit, after which the oldest node will be kicked out when we add another node. For Chris's implementation, he wraps a HashMap and a queue of nodes that he wants to evict.
The queue is a linked list. When we get an node, it goes to the front of the queue. Unfortunately this creates memory and borrowing issues when removing an node from the list and moving it to the front. Chris tried to contain the nodes in an Rc<Node>
and have a link to the next node and a weak link backwards. However in this case he was hitting the heap a lot and he wanted a solution that pushes more of the logic into the struct and avoids the heap.
So instead he made the funniest linked list. It has a single contiguous VecNodeHandle
which is just a newtype for a usize
pointing to the node's place in storage. He could have shifted everything off the Vec, but NodeHandle
is essentially free and it performs in O(1) for removing nodes.
One problem with the LRU Cache is that reading from it is technically writing to it. This occurs because you are moving stuff around in the eviction queue. So it is not thread-safe by default. He solved that by creating a wrapper around LruCache
that is shareable across thread boundaries using Arc<RwLock<LruCache<K, V>>>
.
One final feature of the project is an expiring cache. LruCaches are often used to proxy data sources and adding an expiry is an easy way of saying that you want the data to be "at least as fresh as" some duration.
Hopefully we can see this project packaged up in a crate in the near future.
Const Generics via @DanielPBank
Repo: https://github.com/danielbank/rust-const-generics
Daniel was playing with const generics, mostly by reviewing examples he found online. He did attempt to make an image processing example using const generics but didn't finish in time for the meetup.
Basic Example from the Shipping Const Generics blog post
let data = [1, 2, 3, 4, 5, 6];
let sum1 = data.array_chunks().map(|&[x, y]| x * y).sum::<i32>();
assert_eq!(sum1, (1 * 2) + (3 * 4) + (5 * 6));
let sum2 = data.array_chunks().map(|&[x, y, z]| x * y * z).sum::<i32>();
assert_eq!(sum2, (1 * 2 * 3) + (4 * 5 * 6));
Rectangle Example from the Rust RFC Book
A struct specifying a rectangular array using const generic type parameters. If you attempt to create a rectangular array that does not fit the dimensions, you will get a compile-time error instead of a runtime error.
struct RectangularArray<T, const WIDTH: usize, const HEIGHT: usize> {
array: [[T; WIDTH]; HEIGHT],
}
State Machine Example from /r/rust
See playground here. This example is interesting because we have a type parameter that manages internal state and what functions are available for that state.
This example still needs the #![feature(const_generics)]
feature flag and fails with the following error otherwise. Currently, the supported types are signed and unsigned integer types, booleans, and chars. Other types coming in the future:
error: `&'static str` is forbidden as the type of a const generic parameter
Float Stuff Also Const Generics via @PeterKehl
Repo: https://github.com/precise-rs/unifloat.git
As we were talking about const generics, Peter shared some code he is working that uses const generics. His project provides arbitrary precision floats backed by f32, f64, TwoFloat, and MPFR.
Debugging on AMG8833 Grid-EYE with Rust via @jacobrosenthal
Repo: https://github.com/jacobrosenthal/grideye/tree/cleanup
Finally, still in the vein of const generics, Jacob shared an embedded project he was working on where he used const generics to solve a problem:
Adafruit AMG8833 Grid-EYE Breakout is a sensor from Panasonic featuring an 8x8 array of IR thermal sensors. Jacob had found a Rust driver for the Grid-EYE, but it was not as efficient as he wanted so he did some cleanup on the code.
One of the clean-up items was in an example where he needed to get the raw pixel data from the camera (as u8
s) and group them into u16
s so that he could convert the u16
value to a f32
temperature. (Actually there was another layer of nuance, only 12 bits from the u16
were being used so he had to use u16::from_le_bytes
).
Here he used const generics to group two u8
s into one u16
:
loop {
grideye.get_pixels_temperature_raw(&mut pixels).unwrap();
//group by 2 u8s, turn into u16, then call temperature_u12_to_f32_celsius-> f32
let out = pixels
.array_chunks::<2>()
.map(|chunk| u16::from_le_bytes(*chunk))
.map(|raw| temperature_u12_to_f32_celsius(raw, 0.25))
.collect::<heapless::Vec<f32, N>>();
rprintln!("{:?}", &out);
delay.delay_ms(1000_u16);
}
Previously we would have had to manually create an array, get the two u8
, put them in the array, and pass it on. So using const generics makes this easier to read and work with.
Jacob also shared tools he uses for debugging on embedded, the probe-run and rtt-target crates.
VSCode Rust-analyzer Tips via @craig_jbishop
A couple of tips and tricks for rust-analyzer in VSCode came up.
Toggle On and Off Inlay Hints
The inlay hints in VSCode are a nice way to quickly inspect the types of things, but they can make the code look more verbose than it is. There is a setting to Display Inlay Hints
which works but it is still a little inconvenient to open settings every time you want to show / hide the inlay hints. So here is a cool trick: you can add a keyboard shortcut to toggle the setting on and off. The shortcut uses the Settings Cycler extension, so you will need to have that installed. Afterwards, you can simply add a new entry in your Keyboard Shortcuts:
// Place your key bindings in this file to override the defaults
[
{
"key": "alt+f", // key to press to activate command
"command": "settings.cycle", // `settings.cycle` is the command that's actually being run, from the extension `hoovercj.vscode-settings-cycler`
"when": "editorTextFocus && editorLangId == 'rust'", // this keybinding is only active when (editor is in focus) and (the language is `rust`)
"args": { // these are the arguments passed to `settings.cycle`
"id": "rust-toggle-inlay-hints", // must be unique
"overrideWorkspaceSettings": true,
"values": [ // Note: use the same settings in each values object
{
"rust-analyzer.inlayHints.enable": false // sets the inlay hints off
},
{
"rust-analyzer.inlayHints.enable": true // sets the inlay hints on
}
]
}
}
]
Insert Concrete Type Definitions
You might see an inlay hint for a variable and want to have the concrete type definition in the actual code. You can do this with Cmd .
when the mouse is over the variable.
Crates You Should Know
- ndarray-image: Allows conversion between ndarray's types and image's types
- probe-run: Runs embedded programs just like native ones
- rtt-target: Target side implementation of the RTT (Real-Time Transfer) I/O protocol
- grideye: Rust driver for Grid-EYE / Panasonic AMG88(33)