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

Add support for nullable void return type (`void?`)

Open quad opened this issue 3 years ago • 4 comments

  • :see_no_evil: void? compiles as valid UDL and results in invalid scaffolding

    error[E0277]: the trait bound Option<()>: FfiDefault is not satisfied --> /Users/ssr/dev/wallet/core/target/debug/build/ffi-ff4da05d11cfa7c7/out/core.uniffi.rs:1004:9 | 1004 | uniffi::call_with_result(call_status, || { | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait FfiDefault is not implemented for Option<()> | = help: the following other types implement trait FfiDefault: () *const c_void RustBuffer f32 f64 i16 i32 i64 and 5 others note: required by a bound in call_with_result --> /Users/ssr/dev/wallet/core/.hermit/rust/registry/src/github.com-1ecc6299db9ec823/uniffi-0.19.6/src/ffi/rustcalls.rs:180:8 | 180 | R: FfiDefault, | ^^^^^^^^^^ required by this bound in call_with_result

    error[E0308]: mismatched types --> /Users/ssr/dev/wallet/core/target/debug/build/ffi-ff4da05d11cfa7c7/out/core.uniffi.rs:1004:9 | 1004 | / uniffi::call_with_result(call_status, || { 1005 | | let _retval = r#StartFingerprintEnrollment::r#result( 1006 | | match<std::sync::Arc<r#StartFingerprintEnrollment> as uniffi::FfiConverter>::try_lift(r#ptr) { 1007 | | Ok(ref val) => val, ... | 1013 | | Ok(_retval) 1014 | | }) | |__^ expected (), found enum Option | = note: expected unit type () found enum Option<()> help: consider using a semicolon here | 1014 | }); | + help: try adding a return type | 1000 | ) -> Option<()> { | +++++++++++++* :tada: Kotlin and Swift have a nullable unit types

  • :cry: Python and Ruby don't.

  • something something undefined in WebIDL …

It would be nice to signal whether an operation failed without using a boolean.

┆Issue is synchronized with this Jira Task ┆Issue Number: UNIFFI-199

quad avatar Sep 13 '22 10:09 quad

Thanks for the report!

How do you suggest Python and Ruby would handle this?

It would be nice to signal whether an operation failed without using a boolean.

This isn't a pattern I've ever seen used in Rust either. Is there an actual use-case here? ie, what would Kotlin and Swift lose by just not allowing this construct? That sounds both easier to implement and rationalize about.

mhammond avatar Sep 13 '22 21:09 mhammond

Thanks for the report!

🙇

How do you suggest Python and Ruby would handle this?

📚 I'd throw a warning. Another gotcha on the stack of gotchas!

🤷 But, I don't know! Neither have an optional type. And Optionals (ab)use None and nil.

:trollface: Troll idea for Python, since it has a native tuple type:

  • None => None
  • Some(()) => ()

It would be nice to signal whether an operation failed without using a boolean.

This isn't a pattern I've ever seen used in Rust either.

Optional<()>?

Is there an actual use-case here? ie, what would Kotlin and Swift lose by just not allowing this construct? That sounds both easier to implement and rationalize about.

The use case is signalling that an operation succeeded; the alternative is a void with exceptions.

quad avatar Sep 14 '22 05:09 quad

This isn't a pattern I've ever seen used in Rust either.

Optional<()>?

I understand that's valid, but not aware of where that is actually a pattern someone adopted - do you have any examples?

Similarly, I've never seen a Python function use None vs () to signal something (thank goodness!). I'm less familiar with Kotlin, but can't see a single use of Nothing? in Mozilla's Android browsers.

The use case is signalling that an operation succeeded; the alternative is a void with exceptions.

Right, I guess I'm trying to understand why this complexity is better than a simple bool?

mhammond avatar Sep 14 '22 05:09 mhammond

I understand that's valid, but not aware of where that is actually a pattern someone adopted - do you have any examples?

Beyond the one that motivated this issue? No.

As I noted, we have a set of generic interfaces that return Option<T>; Some(_) indicates a completed operation with a result, and None indicates the operation hasn't completed. Most operations have return values; but, one operation doesn't … it returns ().

(Squint the right way and it's the type signature for a future.)

Similarly, I've never seen a Python function use None vs () to signal something (thank goodness!). I'm less familiar with Kotlin, but can't see a single use of Nothing? in Mozilla's Android browsers.

re: Python, neither have I! Thank goodness indeed!

re: Kotlin, Unit? appears sometimes.

Right, I guess I'm trying to understand why this complexity is better than a simple bool?

This "complexity?" 🤷 I'm not going to argue the point that it's a corner-case. It is!

In my above motivating case, we could have separate bool has_result() and [type] result() functions. The latter would then need throw if called when has_result was false.

What can I say, though, ~WebIDL~ UDL promised a type system that wasn't essentially speaking C++ in different languages.

quad avatar Sep 14 '22 15:09 quad