Copying @bradfitz's comment https://github.com/golang/go/issues/17920#issuecomment-260542015 into a separate proposal.
For Go 2 we should consider changing the Seek method to just take a file position, not a whence argument. We should consider adding SeekFromCurrent and SeekFromEnd, though I suspect they are unnecessary. We should change Seeker similarly. We should remove io.SeekStart, io,SeekCurrent, and io.SeekEnd.
Rationale: the current Seek method is copied from C. It presents three different interfaces, and doesn't make much sense as a single method in Go. People who implement the io.Seeker interface often only implement it for io.SeekStart, and do not correctly handle the other possible values.
See also #17920, which this would replace.
We should consider adding
SeekFromCurrentandSeekFromEnd, though I suspect they are unnecessary.
I looked through all my own code and the Go code at my company.
SeekCurrent is generally only used with 0 to learn the current seek position, and often just to restore it at the end of some operation that really wanted an io.ReaderAt and is hoping that restoring it afterwards won't be too racy.
I support the proposal to reduce the Seek method. That would avoid nasty hacks like this.
That said, I wouldn't discount use-cases for SeekEnd and SeekCurrent. If we provide other interfaces that returned the current offset and the total offset more idiomatically, then the equivalent of SeekEnd and SeekCurrent can be implemented by the user with a little bit of math.
Roughly speaking:
f.Seek(n, SeekStart) => f.Seek(n)
f.Seek(n, SeekCurrent) => f.Seek(f.CurrentOffset()+n)
f.Seek(n, SeekEnd) => f.Seek(f.EndOffset()+n)
EndOffset is just the filesize and can be named Size or something. It is a common piece of information needed with the ReaderAt interface (see #15822).
I much rather see idiomatic methods to access the current offset and end offset than separate SeekFromCurrent and SeekFromEnd methods.
os.File is an API to the OS. Second-guessing the OS model for that API could have unintended consequences. If you must change os.File.Seek, please
a) rename os.File.Seek to .SeekFrom, and
b) add os.File.SeekTo(n) (or .SeekPos?) as an alias for .SeekFrom(n, io.SeekStart).
For an append-only file with an index at file's end, I use seek-from-end to read the index size, then seek-from-current to read the index. Thanks!
Edit:
@dsnet, wouldn't os.File.CurrentOffset & .Size entail a separate syscall?
hoping that restoring it afterwards won't be
tooracy.
If the underlying io.Reader is guaranteed to be unique, then code of that form can use a sync.Mutex or similar synchronization to ensure that it won't be racy at all.
Most helpful comment
I looked through all my own code and the Go code at my company.