Go: proposal: errors: add String

Created on 10 Jun 2020  路  11Comments  路  Source: golang/go

Proposal

Many times I find my self reimplementing this type to provide constant errors:

package errors

type String string

func (s String) Error() string { return string(s) }

I think this would a benefit to add to the standard library to allow for constant errors:

const ErrSentinal errors.String = "an error occurred"

Costs

This does have the possibility of confusing users, as it overlaps with the use case for errors.errorString/Errors.New:

func string() error {
        return errors.String("failed to foo")
}

func new() error {
        return errors.New("failed to foo")
}

Extensions

To simplify errors, we could also remove errors.errorString in favour of errors.String, within errors.New:

package errors

func New(text string) error { return String(text) }

I am not sold on the benefit of this change, other than maybe moving symbols to the data block? Plus, this seems like a big move and removes the possibility extending errors.errorString allowing it to store mutable state.

Alternatives

As an alternative, this symbol could be named errors.Const to signal the intended usage, as opposed to its implementation as a string:

const ErrSentinel errors.Const = "something went wrong"

To resolve confusion about errors.errorString, while also breaking the Go1 compatibility guarantee, this completely clears up the matter and maintains call site compatibility:

package errors

type New string

func (n New) Error() string { return string(s) }
func fail() error {
        return errors.New("failed to foo")
}

P.S. Credit to Dave Cheney for the inspiration.
P.P.S I have published this as part of my expanding errors wrapper package since this does not involve any language changes.

Proposal

Most helpful comment

This is the semantics that errors.New used to have, before Go 1, and we explicitly rejected that implementation, to avoid errors defined in two different packages with the same text accidentally being equal. I don't think we want to put that bug-prone behavior back under a different name, nor under the same name.

You've given no reason why it would be beneficial for errors to be constants. Certainly comparing them would be slower (string compare instead of pointer compare).

I don't remember whether we've optimized errors.New in the compiler so that a global error initialized using errors.New doesn't cause any init time work. We should do that if we haven't already. But doesn't require any visible semantic changes or new API.

All 11 comments

This is the semantics that errors.New used to have, before Go 1, and we explicitly rejected that implementation, to avoid errors defined in two different packages with the same text accidentally being equal. I don't think we want to put that bug-prone behavior back under a different name, nor under the same name.

You've given no reason why it would be beneficial for errors to be constants. Certainly comparing them would be slower (string compare instead of pointer compare).

I don't remember whether we've optimized errors.New in the compiler so that a global error initialized using errors.New doesn't cause any init time work. We should do that if we haven't already. But doesn't require any visible semantic changes or new API.

This is the semantics that errors.New used to have before Go 1.

Interesting, I was not aware of the history, thanks for the context.

You've given no reason why it would be beneficial for errors to be constants. Certainly comparing them would be slower.

Apologies for not making this more clear, my main motivation is to have constant (read: immutable) error sentinels. For me, this outweighs minor perf costs.

You can argue that we are all consenting adults, but then we would not need private symbols or that monkey patching functions is good, as is done in Python. I would also be happy with const var or similar immutability alternatives.

I don't remember whether we've optimized errors.New in the compiler. We should do that if we haven't already.

Assuming this has not already been done, is it worth spinning off a new issue to track?

I agree with the sentiment. Being able to break the world by doing io.EOF = nil is surprising. One should always vet direct and indirect module dependencies, but global errors still feel like a way to break the world that feels unnecessary.

Having said that, if you're interested in read-only variables, you might be interested in previous proposals like https://github.com/golang/go/issues/22876 or https://github.com/golang/go/issues/21130. If Go expands its support for constants or read-only declarations, it's probably best to not limit our scope to just errors.

If the problem is unwanted assigns to global variables, let's work on that instead of focusing on the specific case of errors. For errors, especially given the history, I don't think it makes sense to introduce a second error constructor. Having one is at least 10X better than having two, even if a second might provide marginal benefit.

I guess I thought all of those discussions petered out or were shutdown. If there is active interest and movement on that front, I am game. This was something I saw as feasible today, without sweeping language changes, but I agree there are some caveats.

That being said, I still think there is some benefit to this symbol and would be interested to see the community response, so I will refrain from withdrawing the proposal for now.

@rsc

I don't remember whether we've optimized errors.New in the compiler so that a global error initialized using errors.New doesn't cause any init time work. We should do that if we haven't already. But doesn't require any visible semantic changes or new API.

It seems we're partway there; see #30820.

Retracted by @carnott-snap above.

I will refrain from withdrawing the proposal for now.

@rsc: I explicitly did not retract my proposal and would like to leave it open for further community discussion.

Sorry for the confusion. Reopening. But this will just become a likely decline at the next meeting.

Sorry about claiming it was retracted - misread the ("refrain from withdrawing") comment above.

Now, based on the discussion above, this seems like a likely decline.

No change in consensus, so declined.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

chai2010 picture chai2010  路  216Comments

adg picture adg  路  816Comments

tklauser picture tklauser  路  219Comments

griesemer picture griesemer  路  808Comments

ghost picture ghost  路  222Comments