Kotlin: Conflicting type names for enum variant and variant data
We have an enum like so:
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum SignOutputEnum {
Signature(Bytes),
SignOutput(SignOutput),
}
But we get this error:
yttrium.kt:4352:17 Type mismatch: inferred type is uniffi.yttrium.SignOutput but uniffi.yttrium.SignOutputEnum.SignOutput was expected
public object FfiConverterTypeSignOutputEnum : FfiConverterRustBuffer<SignOutputEnum>{
override fun read(buf: ByteBuffer): SignOutputEnum {
return when(buf.getInt()) {
1 -> SignOutputEnum.Signature(
FfiConverterTypeBytes.read(buf),
)
2 -> SignOutputEnum.SignOutput(
FfiConverterTypeSignOutput.read(buf), // == error on this line ==
)
else -> throw RuntimeException("invalid enum value, something is very wrong!!")
}
}
}
We renamed the SignOutput struct to SignOutputObject for now as a workaround, but it would be nice to not have this conflict in the first place as this is a convenient pattern to have the variant data type name match the enum variant name when there are deeply nested types.
We are currently using commit rev e796e00ad150f8b14b61a859a2e8c6497b35074e and have also encountered the same issue on the latest rev, c7c4809ed0e6bb9195b58855cc7cb211eac13290.
I think this is a dupe of #1137, even though that issue mentions UDL.
Turns out this is Kotlin specific, Python and Swift are fine with it.
Just ran into this for the second or third time! Curious if any plans for a fix
No plans, we'd love a PR from experienced Kotlin users though!
Just had the same problem, to explain the problem in a different way since this might be helpful for others: The Kotlin definition gets generated here https://github.com/mozilla/uniffi-rs/blob/73619b2043c8f6d1b9563a6843ccfa3d633f3196/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt#L60 and for the enum posted in this issue it generates a class like this
sealed class SignOutputEnum {
data class Signature(
val v1: Bytes) : SignOutputEnum() {
companion object
}
data class SignOutput(
val v1: SignOutput) : SignOutputEnum() {
companion object
}
so the problem is that each enum variant is defined as class and the param of it is also a class with the same name SignOutput
I think in theory one might be able to make this work by creating another file and then using a type alias for the class that's used as parameter, but this doesn't seem to be compatible with how the binding is generated (just a single file) The other options I see are:
- Modify the name of the enum variant class (only if a param has the same name), for example with the enum class name combined like
SignOutputEnumSignOutputor just come up with something new likeVariant, soSignOutputVariant. This is a mismatch between Rust and Kotlin and also doesn't look great, but at least it would work out of the box - As Chris mentioned, rename the variant or value on the Rust side
- modify the name of the class that's provided as parameter (just listed for completeness, doing that would be bad) But I'm also not a Kotlin dev, so there might be some other way I missed, maybe one can even use the enum type from Kotlin https://kotlinlang.org/docs/enum-classes.html ?
I haven't looked into the code that generates these files but would it be easier to just always use the fully qualified name? Is this something that would be worth me looking into? @mhammond ?
Like here: https://github.com/mozilla/uniffi-rs/issues/2697#issue-3505250982
but would it be easier to just always use the fully qualified name?
That sounds like it could work. I've no idea how easy that will be, but maybe quite easy.
@mhammond Opened a PR, it was a pretty small change and I tried it out in my project and its working: https://github.com/mozilla/uniffi-rs/pull/2698