Rust Callbacks, DWIW
Sun, 24 Jul 2022
Rust Callbacks, DWIW
This is a short practical post on how to store callbacks in a struct with full generality, allowing the user of your library maximal ease and flexibility. I’ll leave explanations and details to other sources.
The following code stores callbacks of functions Fn(usize) -> usize
, but
any function signature or return value would work fine.
Code is available at https://github.com/duelafn/blog-code/tree/main/2022/rust-callbacks-dwiw
pub struct MyStruct<'a> {
callbacks: Vec<Box<dyn Fn(usize) -> usize + 'a>>,
}
impl<'a> MyStruct<'a> {
pub fn new() -> MyStruct<'a> {
MyStruct { callbacks: Vec::new() }
}
pub fn add_callback(&mut self, func: impl Fn(usize) -> usize + 'a) {
self.callbacks.push(Box::new(func));
}
pub fn trigger(&self, val: usize) {
for (i, cb) in self.callbacks.iter().enumerate() {
println!("{}: {}", i, cb(val));
}
}
}
fn cb1(val: usize) -> usize { val + 1 }
fn main() {
let borrowed_content = String::from("Foo");
// "borrowed_content" must outlive "obj". It is sufficient to declare
// "obj" second, but I've created an explicit scope here for clarity.
{
let mut obj = MyStruct::new();
let moved_content = String::from("Foo");
// Functions (reusable)
obj.add_callback(cb1);
obj.add_callback(cb1);
// Lambdas
obj.add_callback(|n| n + 3);
// Closure owning its variables
obj.add_callback(move |n| n + moved_content.len());
// Closure borrowing non-static variables
obj.add_callback(|n| n + borrowed_content.len());
obj.trigger(6);
}
}
So far as I know, there is no way to replace the Fn(usize) -> usize
with
any type alias or supertrait – I believe one has to spell it out
explicitly each use.