uniffi-rs icon indicating copy to clipboard operation
uniffi-rs copied to clipboard

Can you pass functions as arguments to uniffi?

Open tomadimitrie opened this issue 1 year ago • 4 comments

For example, let's take the following trait:

#[uniffi::export]
trait FFIView: Send + Sync {
    fn render(&self, function: fn());
}

Currently this gives the following compilation error:

the trait bound `fn(): Lift<UniFfiTag>` is not satisfied

I understand that this is a pretty complicated issue and most probably cannot be done directly, but I was wondering if there is any workaround

tomadimitrie avatar Mar 03 '24 00:03 tomadimitrie

You should be able to do that using callbacks or really just traits implemented by foreign languages

badboy avatar Mar 04 '24 11:03 badboy

@badboy that might work, but I have something like this in mind: I am building an UI framework, so let's take a Button for example (I'm on mobile right now so apologies for potential compilation errors):

#[derive(uniffi::Record)]
struct Button {
    on_click: fn()
}

#[uniffi::export]
fn app() -> Button {
    Button { 
        on_click: || {}
    }
}

Swift will call the app() function, parsing the result and displaying a UIButton or whatever. When the native button is pressed, I need to call the Rust implementation of the handler (the struct field).

Yes, I know I can use an Object for this, but the idea is that the handler should be provided by the user. I was also thinking of this:

// make Button an Object instead of a Record

#[uniffi::export]
impl Button {
    fn on_click(&self) {
        (self.on_click)();
    }
}

But I was wondering whether there is a better solution for this

tomadimitrie avatar Mar 04 '24 23:03 tomadimitrie

If the on_click callback is implemented on the Rust side anyway, there's no need to pass it over to the foreign-language side. In that case an object is the better way to handle this.

badboy avatar Mar 05 '24 09:03 badboy

In addition to what @badboy says, the more general solution here is to use a trait - ie, imagine instead of struct Button you had trait Button and the foreign code could supply an implementation of the trait. Thus, either Rust or the foreign code could supply an implementation of on_click(). callback interfaces are just a special case of traits, which is partially why we would recommend using regular traits with foreign bindings rather than callbacks.

mhammond avatar Mar 05 '24 15:03 mhammond

I think we gave a good workaround, and we aren't going to make functions first-class, so let's close this.

mhammond avatar Jun 03 '24 01:06 mhammond