Non-linebuffered stdout

(Much reading later...) Yeah, "I/O Safety" is a mess, isn't it.

Anyway, as @CAD97 just noted, the unambiguous part about I/O Safety is that when an OwnedFd drops, the file descriptor gets closed. From this we can conclude that StdOut and friends cannot hold OwnedFds,[1] since the file descriptors don't close when you drop them.[2] Long story short, file descriptors 0/1/2 are special, and the exact role they play in I/O Safety is not decided; this is the best issue I found on the topic.[3]

What to do in the meanwhile? If you read all the way to the bottom of the I/O Safety docs, you'll find that the exclusivity portions apply to file descriptors specifically, and not any (potentially shared) open file description. And duping file descriptors is also supported. So if working on a duped file descriptor is adequate for your needs, you can (safely!)...

    use std::os::fd::AsFd;
    let owned = std::io::stdout().as_fd().try_clone_to_owned().unwrap();
    let file = std::fs::File::from(owned);
    let tokio_file = tokio::fs::File::from_std(file);

...and that should bypass the buffer (but won't necessarily play nice with other users of StdOut, e.g. you'll almost surely get line-tearing and other intermixing if anything happens to use StdOut).

(I didn't look into whether or not it's adequate for your needs.)


  1. or at least, not ones that correspond to the "root" file descriptors 0, 1, 2 ↩︎

  2. Or they leak the OwnedFd, maybe, but (a) this is like not having one from a user perspective and (b) this is incompatible with the stricter view of I/O Safety as it is currently documented. ↩︎

  3. IMNSHO anything stronger than "you can't close them yourself" is impractical for these file descriptors, but I'm not on any teams. ↩︎

5 Likes