Dart opaque type safety
Ownership and GC
From the moment the opaque type is passed, Rust will own a persistent representation of the dart object (Dart_PersistentHandle
or JsValue
).
This means that while Rust owns DartOpaque
the object will not be cleared by GC.
Also flutter_rust_bridge provides a thread-safe drop for DartOpaque
: Rust delegates the drop to the Dart side using the Dart port.
Dispose flutter_rust_bridge Api before all DartOpaques
are cleaned.
If there is an attempt to delegate the drop to the Dart side after the drop port (Api.dispose()) has been closed. flutter_rust_bridge will issue a warning in the logs, the memory behind the object will leak.
Example
Case 1: loopBack.
Rust api.rs
:
pub fn loop_back(opaque: DartOpaque) -> DartOpaque {
opaque
}
Dart:
String f() => 'Test_String';
var fn = await api.loopBack(opaque: f) as String Function();
expect(fn(), 'Test_String');
Case 2: drop.
Rust api.rs
:
pub fn sync_accept_dart_opaque(opaque: DartOpaque) -> SyncReturn<String> {
drop(opaque);
SyncReturn("test".to_owned())
}
pub fn async_accept_dart_opaque(opaque: DartOpaque) {
drop(opaque);
}
Dart:
// the closure is safely removed on the Rust side (on another thread)
await api.asyncAcceptDartOpaque(opaque: () => 'Test_String');
// the closure is safely removed on the Rust side (on current thread)
api.syncAcceptDartOpaque(opaque: () => 'Test_String');
Case 3: Unwrap.
Rust api.rs
:
/// [DartWrapObject] can be safely retrieved on a dart thread.
pub fn unwrap_dart_opaque(opaque: DartOpaque) -> SyncReturn<String> {
let handle = opaque.try_unwrap().unwrap();
SyncReturn("Test".to_owned())
}
/// [DartWrapObject] cannot be obtained
/// on a thread other than the thread it was created on.
pub fn panic_unwrap_dart_opaque(opaque: DartOpaque) {
let handle = opaque.try_unwrap().unwrap();
}
Dart:
// Rust gets (drop safely) wrap Dart_PersistentHandler (or JsValue).
api.unwrapDartOpaque(opaque: () => 'Test_String');
// We get an error because DartOpaque was passed to another thread.
await expectLater(() => api.panicUnwrapDartOpaque(opaque: () => 'Test_String'), throwsA(isA<FfiException>()));