Expand description
Callback interfaces are traits specified in UDL which can be implemented by foreign languages.
§Using callback interfaces
- Define a Rust trait.
This toy example defines a way of Rust accessing a key-value store exposed by the host operating system (e.g. the key chain).
trait Keychain: Send {
fn get(&self, key: String) -> Option<String>;
fn put(&self, key: String, value: String);
}
- Define a callback interface in the UDL
callback interface Keychain {
string? get(string key);
void put(string key, string data);
};
- And allow it to be passed into Rust.
Here, we define a constructor to pass the keychain to rust, and then another method which may use it.
In UDL:
object Authenticator {
constructor(Keychain keychain);
void login();
}
In Rust:
struct Authenticator {
keychain: Box<dyn Keychain>,
}
impl Authenticator {
pub fn new(keychain: Box<dyn Keychain>) -> Self {
Self { keychain }
}
pub fn login(&self) {
let username = self.keychain.get("username".into());
let password = self.keychain.get("password".into());
}
}
- Create an foreign language implementation of the callback interface.
In this example, here’s a Kotlin implementation.
class AndroidKeychain: Keychain {
override fun get(key: String): String? {
// … elide the implementation.
return value
}
override fun put(key: String) {
// … elide the implementation.
}
}
- Pass the implementation to Rust.
Again, in Kotlin
val authenticator = Authenticator(AndroidKeychain())
authenticator.login()
§How it works.
§High level
Uniffi generates a protocol or interface in client code in the foreign language must implement.
For each callback interface, a CallbackInternals
(on the Foreign Language side) and ForeignCallbackInternals
(on Rust side) manages the process through a ForeignCallback
. There is one ForeignCallback
per callback interface.
Passing a callback interface implementation from foreign language (e.g. AndroidKeychain
) into Rust causes the
KeychainCallbackInternals
to store the instance in a handlemap.
The object handle is passed over to Rust, and used to instantiate a struct KeychainProxy
which implements
the trait. This proxy implementation is generate by Uniffi. The KeychainProxy
object is then passed to
client code as Box<dyn Keychain>
.
Methods on KeychainProxy
objects (e.g. self.keychain.get("username".into())
) encode the arguments into a RustBuffer
.
Using the ForeignCallback
, it calls the CallbackInternals
object on the foreign language side using the
object handle, and the method selector.
The CallbackInternals
object unpacks the arguments from the passed buffer, gets the object out from the handlemap,
and calls the actual implementation of the method.
If there’s a return value, it is packed up in to another RustBuffer
and used as the return value for
ForeignCallback
. The caller of ForeignCallback
, the KeychainProxy
unpacks the returned buffer into the correct
type and then returns to client code.
Structs§
- Struct to hold a foreign callback.
- Used when internal/unexpected error happened when calling a foreign callback, for example when a unknown exception is raised
Enums§
- Result of a foreign callback invocation
Constants§
- The method index used by the Drop trait to communicate to the foreign language side that Rust has finished with it, and it can be deleted from the handle map.