Skip to content

proposal: io/ioutil: move Discard, NopCloser, ReadAll to io #40025

Closed
@rsc

Description

@rsc

io/ioutil exists mainly to avoid import cycles: it can make use of packages that themselves depend on io.

The practical effect of this is that io/ioutil is mainly about convenient OS file access: ReadDir, ReadFile, TempDir, TempFile, and WriteFile. These are here because os was too low level, and io cannot import os. (For more about why io should not depend on os, see “Codebase Refactoring (with help from Go)”, especially section 3.)

The three functions that don't quite fit this bill are Discard, NopCloser, and ReadAll. These are general, useful I/O helpers without dependencies, at least conceptually. They don't need to be in io/ioutil.

It is confusing that nearly all reader and writer adapters—like LimitReader, MultiReader, MultiWriter, TeeReader, SectionReader—are in io, while NopCloser and Discard hide in io/ioutil. They should join the others. I think it was mostly accidental that they ended up in io/ioutil.

It is similarly confusing that the reader and writer helpers—Copy, CopyBuffer, CopyN, ReadAtLeast, ReadFull, WriteString—are all in io, while ReadAll alone hides in io/ioutil. It too should join the others.

In the case of ReadAll, there is a clearer reason why it was relegated to io/ioutil: it imports bytes for access to bytes.Buffer, and bytes imports io. But ReadAll need not use bytes.Buffer, especially now that we have append built in.

Obviously we cannot delete these three from io/ioutil. But we can move the implementations to io and leave wrappers behind. That will be easier for new users, and it lets more packages do general I/O without a dependency on os.

Edit: To be clear, no existing code will break. The old symbols will remain behind, as wrappers of the ones in io.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions