Skip to content

proposal: os: convenience function for one-off Root operations #73168

Open
@neild

Description

@neild

I propose adding the following function:

package os

// InRoot returns a Root that operates on the named directory.
//
// The directory is opened anew at the start of every operation on the Root.
// If the directory cannot be opened, the operation returns an error.
// The directory is closed after every operation. Calling Close on the Root does nothing.
//
// InRoot may be used for one-off operations rooted in a directory.
// For example:
//
//    // Remove name in dir.
//    err := os.InRoot(dir).Remove(name)
func InRoot(name string) *Root

os.Root (#67002) supports performing filesystem operations within the context of some directory.

Performing a single operation in a Root is substantially more verbose than doing so outside of one. Compare:

f, err := os.Create(filepath.Join(base, name))
root, err := os.OpenRoot(base)
if err != nil {
  return err
}
defer root.Close()
f, err := root.Create(name)

If the Root will be reused for many operations, the additional overhead of opening and closing it is not so significant. For a single operation, however, it's enough to be annoying.

We have an os.OpenInRoot convenience function for opening files, but this covers only one operation. We could add similar functions for other operations (os.CreateInRoot, os.RemoveInRoot, etc.), but that would be a lot of new functions.

This has come up in the context of #73126, which proposes adding Root.ReadFile and Root.WriteFile methods: #73126 (comment) asks if we can have top-level convenience functions as well, since opening and closing the root for short operations is inconvenient.

I've also had this raised in a Google-internal discussion: We have a linter which recommends using the github.com/google/safeopen package for certain operations (recommending, for example, that os.Create(filepath.Join(a, b)) could be safeopen.CreateBeneath(a, b)). We're reluctant to change the linter to recommend os.Root in cases where a one-liner becomes several lines opening and closing a Root.

The InRoot function allows performing operations in a root with slightly less verbosity than the non-rooted equivalent using filepath.Join. To continue the above example:

f, err := os.InRoot(base).Create(name)

Metadata

Metadata

Assignees

No one assigned

    Labels

    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposalProposal-Hold

    Type

    No type

    Projects

    Status

    Hold

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions