Skip to content

errors: add ErrUnsupported #41198

Closed
Closed
@ianlancetaylor

Description

@ianlancetaylor

UPDATE: This proposal has shifted from the original description to the one described in comments below.

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.WriteString checks whether an io.Writer has a WriteString method, and either calls it or calls Write.
  • io.Copy checks the source for a WriterTo method, and then checks the destination for a ReaderFrom method.
  • net/http.(*timeoutWriter).Push checks for a Push method, and returns ErrNotSupported if 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions