> For example, "printf" in C can fail, but I have not seen many programs checking its return code!
Okay, I add
if (printf(...) < 0) {
// TODO: Handle error
}
around the printf call. Now what? How do I handle the error? In most realistic scenarios I can imagine I'd either just ignore it and keep going, or print an error and abort (but what if printing an error fails too? Oh no...), or I'll never actually even get the chance to handle the error because my program will get killed by a SIGPIPE.
Seriously, if the printf fails then (barring the malformed format string) that means that the underlying device lost its ability to output data and this ability is most likely not coming back, and your program generally can't do anything with it, or even know about something to do: the functions from the FILE*-family abstract the underlying devices extremely well.
Try it, the canonical C "Hello, world" silently succeeds when given a stdout which rejects all output with an error. The canonical Rust "Hello, world" panics, emitting an error to stderr if stderr works, and doesn't succeed.
Ideally, that properly should be handled in the device driver, with buffering and retries, and not bubble all the way up. But even if it does, you can't reasonably retry the printf call since there is no guarantee that zero data has actually been written.
> multi-byte character conversion fails because the bytes sent and the current locale don’t match
That falls under "malformed format string" category; using printf for MBCS-conversion is almost always a programmer's error.
> printf runs out of memory
That falls under both "stupid library implementation" (printf doesn't need to malloc) and "still can't recover from that".
That’s changing the subject. The comment I replied to claimed “the underlying device lost its ability to output data”, not that retrying wasn’t an option. My point is that the call can fail even though the device is fine.
See above; I never claimed that. Also, this action (in general) isn’t recoverable, but programs sometimes can recover from out of memory conditions, for example by clearing their caches or by freeing a block of memory specially allocated at startup to ensure some recovery from an out of memory condition is possible.
Mmm. Now that I re-read my original comment, it does look like this. But my main point that I wanted to make was "since error from printf() is (almost always) unrecoverable, there is no much point in checking for it, you can't really do anything useful with it, so why do posts that chastise people for not checking it keep getting written?"
And the main reason for a printf() error, in my experience, is indeed the "broken" device which is usually a network socket/pipe or, rarer, a file on a network share or, even rarer, a file on a disk that ran out of free space. That's not something a program can (or even should try to) work around of, that's what its environment should do.
As for the fact that glibc has malloc()-ing implementation of printf() — glibc has many strange implementation decisions. For example, its sscanf() interanlly calls strlen() on the string to scan, so calling e.g. sscanf(1GB_LARGE_BUFFER, "%d", &out) is a bad idea, especially in a loop: https://news.ycombinator.com/item?id=26300451
Okay, I add
around the printf call. Now what? How do I handle the error? In most realistic scenarios I can imagine I'd either just ignore it and keep going, or print an error and abort (but what if printing an error fails too? Oh no...), or I'll never actually even get the chance to handle the error because my program will get killed by a SIGPIPE.Seriously, if the printf fails then (barring the malformed format string) that means that the underlying device lost its ability to output data and this ability is most likely not coming back, and your program generally can't do anything with it, or even know about something to do: the functions from the FILE*-family abstract the underlying devices extremely well.