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

proposal: errors: explicit return type for errors.Join #70989

Closed
JensRantil opened this issue Dec 25, 2024 · 6 comments
Closed

proposal: errors: explicit return type for errors.Join #70989

JensRantil opened this issue Dec 25, 2024 · 6 comments
Labels
Milestone

Comments

@JensRantil
Copy link

Proposal Details

I have a use-case where I would like to signal that a function of mine can return multiple errrors such that I can iterate over them. Returning multiple errors is a common case for validation errors.

I could obviously return []error, but that does not entirely feel like canonical Go to me. I could also implement my own errors.Join where I simply return my own type that implements func (t multiError) Unwrap() []error. However, in the spirit of "Accept interfaces, return concrete types", I am wondering whether it would it make sense to change the signature of errors.Join from

func Join(errs ...error) error

to

func Join(errs ...error) MultipleErrors

where MultipleErrors implements the error interface (ie. fully backwards compatible change) and is identical to joinError.

With this change in mind, a function would be able to signal that it returns multiple errors simply by looking at its interface. For example:

func SumUpPositives(numbers []int) (int, MultipleErrors) {
    if err := validate(numbers); err != nil {
        return 0, err
    }

    sum := 0
    for _, number := range numbers {
        sum += number
    }
    return sum, nil
}

func validate(numbers []int) MultipleErrors {
    var errs []error
    for i, number := range numbers {
        if number <= 0 {
            errs = append(errs, fmt.Errorf("index %d is not a positive number. Positive numbers are only allowed.", i))
        }
    }
    return errors.Join(errs...)
}
@gopherbot gopherbot added this to the Proposal milestone Dec 25, 2024
@JensRantil
Copy link
Author

#47811 looks like a similar proposal, but I feel like this proposal is much smaller in size/scope.

@Jorropo
Copy link
Member

Jorropo commented Dec 25, 2024

It is not possible to change the return type like this because it is an observable breaking change.
Consider this code:

err := errors.Join(errs...)
err = fmt.Errorf("some context %s: %w", str, err)

it'll fail to build if we change the return type because the inferred type for err wont be compatible with fmt.Errorf's return type.

@JensRantil
Copy link
Author

JensRantil commented Dec 25, 2024

@Jorropo I am sorry, but I don't entirely follow. Are you saying that making errors.joinError public and adding it to errors.Join return type would somehow break fmt.Errorf(...) backwards compability? errors.JoinError would implement func Unwrap() []error just like before, and according to https://pkg.go.dev/fmt#Errorf, the %w verb will not rely on the type, but look at the Unwrap method. I might of course be missing something here.

@Jorropo
Copy link
Member

Jorropo commented Dec 25, 2024

The only critical problem is changing the return type of errors.Join.

The problem I am demonstrating is not with fmt.Errorf it is with implicit type conversions of the = operator.
Here is more minimal code if you want:

err := errors.Join(nil)
var errr error
err = errr

this would compile before your change but not after.

Tl;Dr

you can't change the signature of errors.Join, random changes you could do to the proposal:

  • remove that part and rely on people manually instantiating errors.MultipleErrors
  • deprecate errors.Join and make an errors.Join2 (better name pending) function with the updated signature
  • ...

@seankhliao
Copy link
Member

closing as not feasible

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Dec 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants