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

Swift Feature: "Main thread" attribute for object

Open lexoliu opened this issue 8 months ago • 3 comments

The UniFFI user guide states:

Example: 

We insist that all object instances exposed to foreign-language code be Sync and Send, 

so that they’re safe to access regardless of the threading model of the calling code.

We do not allow thread-safety guarantees to be deferred to assumptions about how the code is called.

However, if an object is created on the main thread and all its methods are also called on the main thread, then even if the object does not satisfy Send, it is still thread-safe because it remains confined to a single thread.

Rough Design

Rust side


#[uniffi::main]
struct Text{
    ...
}

#[uniffi::export(main)]
impl Text{
    fn get_text(&self) -> String{
       ...
    }
}

Swift side

@Mainactor
class Text{
    // Make sure object only be used on main thread...
    func get_text() -> String{
        ...
    }

    deinit{// class may be deallocated on other thread...so we need call `Drop::drop` on main thread
        Task{@Mainactor in
             // call `Drop::drop` here...
        }
    }
}

Since Swift 5.5, we have the @MainActor attribute. UniFFI can leverage this by adding @MainActor to the generated code for such cases.

Why

This feature is particularly useful for UI handling. Most native UI frameworks require updates to be performed on the main thread, meaning our Rust code may always run on the main thread. Allowing a !Send object could reduce unnecessary synchronization overhead.

Reminder

An object may be deallocated (deinit) on a different thread. Therefore, on the foreign language side, we need to ensure that Rust’s Drop() runs on the main thread.

Problem

I am not familar with C# and Kotlin, so I don't know whether they have equivalent of @Mainactor

lexoliu avatar Feb 11 '25 09:02 lexoliu