Ohh, I see. Yeah, on rare occasions writev() is worth the hassle, but it doesn't really get around the performance loss from unbuffered I/O; if you're calling println(f.size, " ", f.date, " ", f.filename) in a loop that overwrites f 100 times, writev() reduces that from 600 syscalls to 100, but bufio reduces it to 1.
I do understand why io::file needs to be separate from io::stream. (The io documentation introduction gives an explanation of what you're explaining above; I'd additionally offer the examples of a gzip stream, a UTF-8-decoded stream on an ISO-8859-1 text file, and maybe a stream that feeds into in-process terminal emulator logic.) I was asking why io::handle does. If you're writing code that takes an io::handle, generally the code cannot rely on the fact that io::file can be used for select() or ioctl() or getpeername(); if it needed to do that, you would have written it to take an io::file, not an io::handle. So, if your code is only going to invoke io::stream-like operations on the io::handle, it would be simpler if its argument were an io::stream instead of an io::handle.
But what if you want to give it an io::file? Well, it's easy enough to wrap an io::file in an io::stream that just invokes the appropriate rt operations, and in most languages the only per-call cost of doing that is that your function call is indirect (mov 12(%ebx), %ecx; call %ecx) rather than direct (call Zn#]io31337write). In fact, most code would be more* efficient that way, because right now if someone gives you an io::handle and you want to read from it, you're usually going to call io::read on it, adding an extra level of function call around the indirect call, because that's simpler than duplicating io::read's conditional call to rt::read in your own code. But dynamically most read and write calls will probably be on a bufio::bufstream (or some other io::stream), so io::read is just going to call .reader().
Does io::handle maybe exist only as an optimization for sendfile()?
Maybe it's too late for such changes, given the amount of existing Hare code, and this qualifies as bikeshedding, and if so, I apologize.