-
Notifications
You must be signed in to change notification settings - Fork 17.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: io/fs,path/filepath: add iterator form of WalkDir #64341
Comments
Any reason not to return the error as a second value? like this: type WalkDirEntry struct {
Path string
Entry fs.DirEntry
skipDir *bool
}
func WalkDirIter(fsys fs.FS, root string) func(func(WalkDirEntry, error) bool) { Seems like the conclusion of #61405 is to return errors this way. #61405 (comment) |
That was @Merovius's suggestion too. I wasn't sure, because the Path and Entry fields can be valid even when the error isn't, making the docs a bit awkward to write, which made me think it might not be the correct approach. Nonetheless, here's a stab at it. Maybe it's not so awkward after all.
|
I am also not a big fan of the |
I've observed (https://sourcegraph.com/search?q=context%3Aglobal+lang%3Ago+fs.WalkDir&patternType=standard&sm=1&groupBy=repo) that most This lack of checking obviously leads to a panic. There are also two missing checks in two tests in the Go repository. I suggest not passing the error as a parameter, that is, leaving only one parameter, but ensuring that files, err := fs.WalkDirIter(fsys, root)
if err != nil {
// stat on the root directory failed
...
}
for file := range files {
_ = file.Entry.Name() // this cannot panic because file.Entry is always guaranteed to be non-nil
} |
@gazerro Yes, I tend to agree, although it's a bit unfortunate that that makes it considerably harder (or perhaps no possible?) to implement |
Yes, I agree. It would be more appropriate to implement |
I also suggest naming the new function entries, err := fs.DirWalker(fsys, root)
if err != nil {
// stat on the root directory failed
...
}
for entry := range entries {
// ...
} |
Individual subdirectories and files can fail, so I don’t know what it saves you to return an error for the initial stat. |
@carlmjohnson if the first stat fails, the I have noticed many cases (https://sourcegraph.com/search?q=context%3Aglobal+lang%3Ago+fs.WalkDir&patternType=standard&sm=1&groupBy=repo) where this check is not done. In addition, this is my opinion, I find it incorrect for the function to return an iterator on a root directory that does not exist. |
I think it would be better to focus solely on the use case of reading file contents, since the design of Go's range-over-func itself is biased towards iterators that are simple rather than featureful: package fs
type Data interface {
Stat() FileInfo
ReadAll() []byte
}
func Files(fsys FS, err *error) iter.Seq2[string, Data] Usage would be something like: func main() {
var err error
for pathName, data := range fs.Files(os.DirFS("."), &err) {
if !strings.HasSuffix(pathName, ".json") {
continue
}
// returns []byte{} - not nil - on error
// errors are automatically collected at the end
json := data.ReadAll()
fmt.Println(string(json))
}
// loop automatically terminates early if any error occurs
if err != nil {
fmt.Println(err)
}
} |
It's often more convenient to use a for loop rather than a callback-based API, and fs.WalkDir and filepath.Walk are both callback-based APIs. I've been using github.com/kr/fs for years to work around this.
The new iterate-over-func functionality (#61405) now makes it straightforward to adapt the stdlib functions into iterators, so perhaps we could provide iterator-based APIs as part of the standard library now.
Here's a straw-man implementation:
The main point of contention from my point of view is the semantics of the SkipDir method. Calling a method on an iterated item to determine the subsequent course of iteration seems somewhat unusual, but I don't see any other decent alternative.
[Edited to remove incorrect remarks about GC behavior].
The text was updated successfully, but these errors were encountered: