-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
UPDATE: This proposal has shifted from the original description to the one described in comments below.
- errors: add ErrUnsupported #41198 (comment)
- errors: add ErrUnsupported #41198 (comment)
- errors: add ErrUnsupported #41198 (comment)
- errors: add ErrUnsupported #41198 (comment)
Go has developed a pattern in which certain interfaces permit their implementing types to provide optional methods. Those optional methods are used if available, and otherwise a generic mechanism is used.
For example:
io.WriteStringchecks whether anio.Writerhas aWriteStringmethod, and either calls it or callsWrite.io.Copychecks the source for aWriterTomethod, and then checks the destination for aReaderFrommethod.net/http.(*timeoutWriter).Pushchecks for aPushmethod, and returnsErrNotSupportedif not found.
The io/fs proposal (#41190) proposes various other optional methods, such as ReadFile, where there is again a generic implementation if the method is not defined.
The use of WriterTo and ReaderFrom by io.Copy is awkward, because in some cases whether the implementation is available or not is only known at run time. For example, this happens for os.(*File).ReadFrom, which uses the copy_file_range system call which is only available in certain cases (see the error handling in https://golang.org/src/internal/poll/copy_file_range_linux.go). When os.(*File).ReadFrom is called, but copy_file_range is not available, the ReadFrom method falls back to a generic form of io.Copy. This loses the buffer used by io.CopyBuffer, leading to release notes like https://golang.org/doc/go1.15#os, leading in turn to awkward code and, for people who don't read the release notes, occasional surprising performance loss.
The use of optional methods in the io/fs proposal seems likely to lead to awkwardness with fs middleware, which must provide optional methods to support higher performance, but must then fall back to generic implementations with the underlying fs does not provide the method.
For any given method, it is of course possible to add a result parameter indicating whether the method is supported. However, this doesn't help existing methods. And in any case there is already a result parameter we can use: the error result.
I propose that we add a new value errors.ErrUnimplemented whose purpose is for an optional method to indicate that although the method exists at compile time, it turns out to not be available at run time. This will provide a standard well-understood mechanism for optional methods to indicate that they are not available. Callers will explicitly check for the error and, if found, fall back to the generic syntax.
In normal use this error will not be returned to the program. That will only happen if the program calls one of these methods directly, which is not the common case.
I propose that the implementation be simply the equivalent of
var ErrUnimplemented = errors.New("unimplemented operation")
Adding this error is a simple change. The only goal is to provide a common agreed upon way for methods to indicate whether they are not available at run time.
Changing ReadFrom and WriteTo and similar methods to return ErrUnimplemented in some cases will be a deeper change, as that could cause some programs that currently work to fail unexpectedly. I think the overall effect on the ecosystem would be beneficial, in avoiding problems like the one with io.CopyBuffer, but I can see reasonable counter arguments.