This method is available for TCP and Unix streams but not on File. The obvious objection in case of file is that files cannot be non-blocking. However that's not true if the file is actually a named pipe or character device. I think a method with appropriate warning in documentation would be reasonable. (or perhaps slightly different method name)
The expectation would be that calling this method on a File will then potentially lead read/write/etc to return EAGAIN? (And you'd have to arrange to use poll/epoll/etc to check for readiness, or some other means to make sure something is ready.)
I remember looking a bit at this previously[1] and finding code in std that would cause significant issues if it were to encounter a non-blocking File. Specifically, IIRC it's writing on Windows which has to abort if a write doesn't complete synchronously since the Windows async file IO can be completion based instead of readiness based.
So unfortunately this would be a very scary thing to make available cross-platform. It also isn't immediately clear that the caller needs to set up readiness interest (or how to connect such to the tried operation if necessary).
The amount of existing code written that expects not to encounter a would-block IO error on a File is large enough that breaking that assumption, while not necessarily unsound, is still spooky.
Context: considering using std File handles in a toy async runtime. âŠī¸
Yes, trying to read from a pipe when the other side didn't provide data returns EAGAIN/EWOULDBLOK. I believe it should be the same for character devices.
I thought Windows had similar mechanisms but if it's that bad then I guess the method should be on FileExt trait available on Unix. Also I expect the documentation to say something like "this will still block on regular files and block devices, the caller is responsible for handling this appropriately".
To expand on this a bit, regular files, named pipes and character devices are all opened with the same syscall: open, which in Rust is called by std::fs::File::open. So you don't know what you've opened until you call metadata on the opened file.
But having separate types is not entirely without merit. One would usually check the type first and then act accordingly if special handling is expected. However there are times when one wants to treat different file types the same.
Off topic perhaps but that's specifically because the std Write implementation is designed to be synchronous and people are keen for a single write to boil down to a single syscall where possible. But even so it will handle an async File in the most common case (i.e. the handle itself is waitable and isn't being used simultaneously by multiple threads). It is certainly possible to handle both async and sync files using the same function but then you need to add some synchronization mechanism. E.g. an event object, a completion routine, an IoRing.
If we had a Stream type (or somesuch) then that kind of complexity could be handled up front but, as it is, Write is implemented directly for File (which is merely a thin wrapper around HANDLE) and each call to write is independent of any other call to write. So there's a trade-off.