Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can you pass functions as arguments to uniffi? #2003

Closed
tomadimitrie opened this issue Mar 3, 2024 · 5 comments
Closed

Can you pass functions as arguments to uniffi? #2003

tomadimitrie opened this issue Mar 3, 2024 · 5 comments

Comments

@tomadimitrie
Copy link

tomadimitrie commented Mar 3, 2024

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

@badboy
Copy link
Member

badboy commented Mar 4, 2024

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

@tomadimitrie
Copy link
Author

@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

@badboy
Copy link
Member

badboy commented Mar 5, 2024

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.

@mhammond
Copy link
Member

mhammond commented Mar 5, 2024

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
Copy link
Member

mhammond commented Jun 3, 2024

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

@mhammond mhammond closed this as not planned Won't fix, can't repro, duplicate, stale Jun 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants