Functional Interfaces
UniFFI has good support for callback interfaces, allowing calling code to supply its own logic to be called from Rust. How to make this work on the other-language side was relatively obvious to me: you just implement the interface. However, how to make this smooth in Rust took a moment to figure out, so I figured I'd make a note.
A UniFFI callback interface looks (using the proc macros) like this:
#[uniffi::export(callback_interface)]
pub trait MyCallback: Send + Sync {
fn consume(&self, value: MyValue);
}
(where MyValue has #[uniffi::export] as well)
and some function which takes it (boxed, because we can't know the implementing type at compile
time, so it's gotta be a dyn rather than being able to use a generic/impl Trait) like so:
#[uniffi::export]
pub fn do_it(doer: Box<dyn MyCallback>) {
doer.consume(some_value);
}
Which is all well and good. But using this from Rust is a bit of a pain - there's just one method, so it should be possible to just yunk in a closure. But if you wanna do that you gotta tell Rust that's okay, it doesn't infer it like Java's functional interfaces. Here's how you do that:
impl<T> MyCallback for T
where
T: Fn(MyValue) -> () + Send + Sync
{
fn consume(&self, value: MyValue) {
self(value)
}
}
Getting the syntax right on this took a sec but it's fairly self-explanatory if you're used to Rust.
I also went down a road of trying to use impl From, which doesn't really work out (try it
yourself if you want to know why). Anyway, now you can call do_it from Rust and give it a boxed,
but otherwise plain, closure:
do_it(Box::new(|_it| {}))