Is println! technical debt?

In light of

is println! a code smell / technical debt ?

Devil's advocate could argue: How often are we 100% sure we want something to go to stdout rather than an output file, some gui element, or a logging framework, etc ...

However, if we take this to the logical extreme, all println! become write!, and in the main function, we specify where it writes to (which is also weird-ish).

1 Like

No more than something like .unwrap() is.
They both have the same raison d'etre: convenience.

4 Likes

It depends on what kind of program you're writing. Operating systems provide ways for users to decide where stdout goes, so there's no fundamental reason to duplicate this functionality. That said, it can be a problem if you write multiple kinds of output to stdout that should be separable, and redirecting stdout over then network can be a pain, e.g. for logging.

1 Like

Once you're writing something important enough to unit test the output, then yes, you probably shouldn't be using println! any more.

But while that's super-important for the top-100-most-downloaded crates, it's probably not important for most rust programs written. If you're writing a quick thing, just println!. It's fine.

(In the extreme case, when writing Hello World, you obviously don't want to force people to use writeln! and stdout and such.)

8 Likes

One of the nice features of systemd is that it sends stdout into logs automatically.

In the context of this thread I can see why you would call that a nice feature.
However I've been burned by the dark side of that feature: it once filled up my boot drive to 100% with spurious logs. So the next day when I booted up my pc I suddenly had some troubleshooting to do.
So all I'm saying is, there's no such thing as a free lunch.

No, not in itself. I think there is actually a pretty clear heuristics for whether you should be printing to stdout.

If you are producing exclusively human-readable output, e.g. non-logged diagnostics or interactive (e.g. TUI) content from a stand-alone binary, then do feel free to print to stdout/stderr unconditionally. This is because it is highly unlikely that anyone trying to parse or use the output in any other way is using the system correctly/robustly/as intended. Generally, diagnostic/status messages of command line tools are best assumed to be unspecified and subject to change, so trying to e.g. capture the output of some command just to extract machine-readable information from it is almost certainly going to end with pain and suffering.

And even in the rare case when this is not true, it's trivial to redirect standard streams of a stand-alone command to a file or to a pipe, because the OS itself supports that.

Once you are producing machine-readable output, though, you should anticipate that someone will try to parse it (that's the whole point). So that means that you should probably think of redirection, right? Actually, at that point, I would argue that you should expose the code in question as a library, not merely as a command, and then of course you should prefer writing everything to a generic io::Write instead of stdout/stderr.

As my programs grow, I always end up removing all println!s, because e.g. I add a --verbose flag and need to toggle them, or I adopt a proper logging framework, or switch to richer typed errors and warnings that are output-independent (localized or inserted into some JSON), or I start using threads and println!s get jumbled. I've had one program that I've used with redirected stdout and println! sometimes panicked on a Broken Pipe, so error handling is a real concern too!

So println! is fine for hello-world sized utilities, but becomes more of a problem as program's requirements grow.

7 Likes