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

for v11: NewFailureResponse() should return error not *apiresponses.FailureResponse #256

Closed
blgm opened this issue Jun 6, 2023 · 3 comments · Fixed by #257
Closed

for v11: NewFailureResponse() should return error not *apiresponses.FailureResponse #256

blgm opened this issue Jun 6, 2023 · 3 comments · Fixed by #257
Labels

Comments

@blgm
Copy link
Member

blgm commented Jun 6, 2023

Describe the bug

See: #252 (comment)

Reproduction steps

See: #252 (comment)

Expected behavior

A New... function arguably should return the type that it mentions, but in this case returning an error type may make more sense as it would encourage the use of var error rather than var *apierrors.FailureResponse which is not idiomatic Go, and can lead to strange situations.

Additional context

Would be a breaking change, so wait until v11

@blgm blgm added the bug label Jun 6, 2023
@cf-gitbot
Copy link
Member

We have created an issue in Pivotal Tracker to manage this:

https://www.pivotaltracker.com/story/show/185344128

The labels on this github issue will be updated when the story is started.

@blgm
Copy link
Member Author

blgm commented Jun 6, 2023

Delay until v11

blgm added a commit that referenced this issue Jun 7, 2023
The function NewFailureResponse() creates an error. The [Go FAQ](https://go.dev/doc/faq#nil_error) says:

> It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly. As an example, os.Open returns an error even though, if not nil, it's always of concrete type *os.PathError.

Returning the concrete type can result in subtle failures. For example,
consider the code:
```go
func something() error {
	var err *apiresponses.FailureResponse
	if 1 != 1 {
		err = apiresponses.NewFailureResponse(errors.New("something bad has happened with the universe"), http.StatusInternalServerError, "log-key")
	}
	return err
}

func main() {
	if err := something(); err != nil {
		fmt.Printf("bad thing: %s\n", err.Error())
	}
}
```

You might expect this to print nothing since 1 does not equal 1. But
actually it panics. This is because the nil
*apiresponses.FailureResponse is not equal to `nil` because it has a
type, but it does have a nil pointer so it panics.

If we replace `var err *apiresponses.FailureResponse` with `var err error`
then everything works as expected. By returning an `error` type from
NewFailureResponse(), the failing scenario doesn't compile.

As with many fixes, this could be considered to be a breaking change.
But it didn't require code changes on the brokers that I tested with,
and if it does break anyone, then it's likely because they have risky
code that has the potential to panic in a counterintuitive way. So
overall I think the benefit of making this change it worth the risk.

Resolves #252
Resolves #256
@blgm
Copy link
Member Author

blgm commented Jun 8, 2023

Decided not to wait until v11. On reflection, this is unlikely to break many users, as anyone using an apiresponses.FailureResponse pointer is likely to be seeing failures. See PR #257 for resolution.

@blgm blgm closed this as completed Jun 8, 2023
zucchinidev pushed a commit that referenced this issue Sep 10, 2023
The function NewFailureResponse() creates an error. The [Go FAQ](https://go.dev/doc/faq#nil_error) says:

> It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly. As an example, os.Open returns an error even though, if not nil, it's always of concrete type *os.PathError.

Returning the concrete type can result in subtle failures. For example,
consider the code:
```go
func something() error {
	var err *apiresponses.FailureResponse
	if 1 != 1 {
		err = apiresponses.NewFailureResponse(errors.New("something bad has happened with the universe"), http.StatusInternalServerError, "log-key")
	}
	return err
}

func main() {
	if err := something(); err != nil {
		fmt.Printf("bad thing: %s\n", err.Error())
	}
}
```

You might expect this to print nothing since 1 does not equal 1. But
actually it panics. This is because the nil
*apiresponses.FailureResponse is not equal to `nil` because it has a
type, but it does have a nil pointer so it panics.

If we replace `var err *apiresponses.FailureResponse` with `var err error`
then everything works as expected. By returning an `error` type from
NewFailureResponse(), the failing scenario doesn't compile.

As with many fixes, this could be considered to be a breaking change.
But it didn't require code changes on the brokers that I tested with,
and if it does break anyone, then it's likely because they have risky
code that has the potential to panic in a counterintuitive way. So
overall I think the benefit of making this change it worth the risk.

Resolves #252
Resolves #256
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants