Use - as stdin/stdout

Viewed 338

A lot of command-line tools let you use - for standard input or standard output. Is there an idiomatic way to support that in Rust?

It looks like the most common way to handle command-line arguments is clap. If I just want to handle paths and donʼt want to special-case -, I can use

use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

use clap::Parser;

#[derive(clap::Parser, Debug)]
struct Args {
    #[clap(parse(from_os_str))]
    output: PathBuf,
}

fn main() -> std::io::Result<()> {
    let args = Args::parse();
    let mut file = File::create(args.output)?;
    file.write_all(b"Hello, world!")?;
    Ok(())
} 

However, to handle the case of - being stdout, itʼs more complicated. The type of file now needs to be Box<dyn Write> since stdout() is not a File, and itʼs somewhat complicated to set it correctly. Not difficult, but the sort of boilerplate that needs to be copied several times and is easy to mess up.

Is there an idiomatic way to handle this?

1 Answers

This is something you just have to write into your code. In many cases, it wouldn't be clear whether - should represent standard input or standard output, and it requires the context of the program to interpret it correctly.

You do in fact need to use a Box<dyn Write> (or &dyn Write or similar) in this case. If you're using Unix, you can create a File object from standard input or output by creating one using FromRawFd (and something similar, but not identical, on Windows), but you should avoid doing that, because when you drop a File, the file descriptor is closed, which is not the right behavior here.

In fact, accidentally closing one of the standard file descriptors can actually lead to data corruption, since if another file is opened, it will be opened with the lowest possible value (e.g., 1, for standard output), and then things like println! might accidentally print to the newly opened file.

So using a dyn Write is the right option here. In most cases, the performance impact will not be noticeable.

Related