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

Use Rust's Error::source() method to implement Unwrap() where possible #61

Open
BatmanAoD opened this issue Oct 21, 2024 · 2 comments
Open

Comments

@BatmanAoD
Copy link

BatmanAoD commented Oct 21, 2024

uniffi-bindgen-go provides some support for errors.Is, by generating a Go error value named Err<typename><variant> for each member of a "flat" UniFFI error.

However, not all UniFFI errors are "flat". Given this Rust error:

#[derive(Debug, thiserror::Error, uniffi::Error)]
enum InnerError {
    RootCause,
}

#[derive(Debug, thiserror::Error, uniffi::Error)]
enum OuterError {
    Inner{
        #[source]
        inner: InnerError,
    },
}

It should be possible to check for a value of type OuterError::Inner{inner: InnerError::RootCause} using this Go expression:

errors.Is(err, bindings.ErrInnerErrorRootCause)

To implement this, I expect that the Go version of OuterError would look something like this:

type OuterErrorVariant int
var (
     OuterErrorVariantInner OuterErrorVariant = iota
)

type OuterErrorInner struct {
    Inner: <opaque handle to Rust error, ideally>
}

func (err OuterErrorInner) Unwrap() error {
    source := err.Inner.source()
    // "lift" `source` back into a Go error
}

type OuterError struct {
    _variant: OuterErrorVariant
    data: any
}

func (err OuterError) Unwrap() error {
    switch err._variant {
        case OuterErrorVariantInner:
            err.data.(OuterErrorInner).Unwrap()
        // default case and/or error handling
    }
}
@BatmanAoD
Copy link
Author

Actually, it looks like the generated Go code without #[uniffi(flat_error)]` always has a compile error:

bindings.go:6093:13: cannot use FfiConverterTypeFooBarErrorINSTANCE.Read(reader) (value of type error) as FooBarError value in struct literal: need type assertion

@BatmanAoD
Copy link
Author

With flat_error, it appears that even using #[transparent], there's no way for Go to see the variant of a "sub-enum"; i.e.:

#[derive(Debug, thiserror::Error, uniffi::Error)]
enum InnerError {
    RootCause,
}

#[derive(Debug, thiserror::Error, uniffi::Error)]
#[uniffi(flat_error)]
enum OuterError {
    #[transparent]
    Inner(#[from] InnerError),
}

This Go expression should return true for an error created in Rust as OuterError::Inner(InnerError::RootCause):

errors.Is(err, bindings.ErrInnerErrorRootCause)

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

1 participant