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

Enum with associated data containing a callback interface produces incorrect Swit code.

Open Zollerboy1 opened this issue 1 year ago • 5 comments

UniFFI automatically declares enums in Swift as being Equatable and Hashable. This results in a compiler error when the associated data of the enum contains a callback interface.

Example Rust code:

pub trait Foo: Send + Sync {
    fn value(&self) -> u32;
}

pub enum Bar {
    Baz { foo: Box<dyn Foo> },
}

pub fn get_foo(bar: Bar) -> u32 {
    match bar {
        Bar::Baz { foo } => foo.value(),
    }
}

Example UDL file:

namespace test_uni_ffi {
    u32 get_foo(Bar bar);
};

callback interface Foo {
    u32 value();
};

[Enum]
interface Bar {
    Baz(Foo foo);
};

Relevant part of the produced Swift code:

public protocol Foo: AnyObject {
    func value() -> UInt32
}

public enum Bar {
    case baz(foo: Foo)
}

extension Bar: Equatable, Hashable {}
// error: type 'Bar' does not conform to protocol 'Equatable'
// error: type 'Bar' does not conform to protocol 'Hashable'

public func getFoo(bar: Bar) -> UInt32 {
    // ...
}

Zollerboy1 avatar Jul 26 '23 11:07 Zollerboy1

I only gave this a very shallow look just now. We do try to avoid those implementations for types that contain other object references: https://github.com/mozilla/uniffi-rs/blob/1696344a8f1643cfa1a36a3fac201340b0b62a98/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift

Maybe we should extend that.

badboy avatar Aug 09 '23 10:08 badboy

+1 I have a struct

#[derive(uniffi::Object)]
pub struct Animal {
     pub name: String;
}
#[derive(uniffi::Record)]
pub struct Person {
     pub animal: Arc<Animal>
}

In swift, this generates a Person which conforms to equatable, but Animal doesn't

pierre-wehbe avatar Aug 06 '24 19:08 pierre-wehbe

In swift, this generates a Person which conforms to equatable, but Animal doesn't

That's because Swift is able to directly compare each field for the record. However, it is possible to expose some builtin Rust traits on an interface, such as Eq etc, which Swift should then support.

https://mozilla.github.io/uniffi-rs/latest/udl/interfaces.html#exposing-methods-from-standard-rust-traits

Here is an example of Rust code doing that and of the swift code testing it

(In fact with this capability, I suspect this issue should be closed?)

mhammond avatar Aug 06 '24 20:08 mhammond

(In fact with this capability, I suspect this issue should be closed?)

hrm, probably not - the original issue is about interfaces inside records, so that's probably still true. Using Eq might help interfaces in that case, but probably not traits/callbacks.

mhammond avatar Aug 07 '24 01:08 mhammond

@mhammond My current workaround is to implement Equatable/Hashable as an extension

extension Animal: Equatable, Hashable {
... // this tend to be a bit more custom for Objects
}

pierre-wehbe avatar Aug 07 '24 02:08 pierre-wehbe