mozilla / uniffi-rs

a multi-language bindings generator for rust
https://mozilla.github.io/uniffi-rs/
Mozilla Public License 2.0
2.48k stars 211 forks source link

How to share a reference to a struct with UniFFI? #2147

Closed pacu closed 2 weeks ago

pacu commented 2 weeks ago

I have this SecretPackage struct that the client of my FFI code needs to get ahold of.

This struct doesn't have to be ever serialized, just kept in memory for a few seconds to then use it through the FFI.

I need the reference to be kept alive

#[derive(uniffi::Object)]
struct DKGPartOnePackages {
    pub secret: DKGSecretPackage,
    pub package: DKGPackage
}

#[derive(uniffi::Object)]
struct DKGSecretPackage {
    data: SecretPackage # <--- this is the real secret that 
}

#[uniffi::export]
fn stepOne() -> DKGSecretPackage {
/// some code
}

#[uniffi::export]
fn stepTwo(secret: DKGSecretPackage) -> Bool {
/// some code
}

So the ffi client would call

let secret = stepOne()
// some other code

let productOfTheSecret = stepTwo(secret)

Secret has to be kept in memory and not used by another thing that calling stepTwo() so this secret rust struct does not have any serialization methods because it's an implementation detail. It's a real secret :).

how can I make code on the Swift/Go side of UniFFI hold the secret until it's time to use it? how to pass it through the FFI?

I'm currently getting this error

the trait bound `DKGPartOnePackages: LowerReturn<UniFfiTag>` is not satisfied
the following other types implement trait `LowerReturn<UT>`:
  bool
  i8
  i16
  i32
  i64
  u8
  u16
  u32
and 34 others
required for `Result<DKGPartOnePackages, FrostError>` to implement `LowerReturn<UniFfiTag
mhammond commented 2 weeks ago

You probably just need Arcs around your objects - eg, fn stepOne() -> Arc<DKGSecretPackage>; (and obviously wrapping it in an Arc at the right time)

pacu commented 2 weeks ago

Thank you for your quick response @mhammond! I really appreciate it!

I assume that UniFFI will take care of the Swift/Go side to hold the Arc reference so it's not deallocated, is this correct?

mhammond commented 2 weeks ago

Yes, the foreign side knows it is holding a reference counted reference and will drop the reference at the appropriate time in the foreign side's life-cycle. However, as with references and reference counting in general, it does mean it might not be deterministic (eg, we have no idea if the foreign side might stash the reference into a global variable)