When you open a file that does not exist you get an appropriate NotFound error kind, however the message "No such file or directory" is missing the name of the file or directory that is missing. If your program is loading a lot of files it would be very helpful for the user to see which one of them is missing. Sure, one can always customize the error message and in a final product we should not unwrap() in such a case, but why not make the default user experience as best as possible?
but why not make the default user experience as best as possible?
Because that would incur an allocation cost to carry the path, which has to be owned by the error message. io::Error is a slim representation of operating system errors, and those do not contain the path that failed. So if you want it to be userfriendly you have to enrich the error with some context, as various crates offer.
In other words, it's possible to add the data. But if it where there by default then everyone would pay the cost because it can't be removed.
Some kinds of filesystem access patterns encounter more error cases than success cases (e.g. probing a bunch of paths until a file is found), so making errors more expensive would affect them greatly.
(Slightly duplicate answer, I started writing this before @the8472 posted their reply )
One problem here could be overhead in error handling:
The std::io::Error type currently uses a compact representation (only 1 pointer large), which ensures it's cheap to pass around, but it also aids in minimizing the need for incurring any overhead in terms of allocations, in many common cases. For many functions, like File::open, this means the raw OS error code that the std::io::Error is created from is simply slightly bit-shifted and tagged, but otherwise left untouched. If the error message was supposed to mention the file name, with a type like std::io::Error: 'static, there would first need to be a new allocation created in order to remember this file name as part of the error.
Thank you for the GitHub link, I read through it and it was really interesting!
It mentions crates.io: Rust Package Registry which I may use as a drop in for std::fs to get clearer error messages.
In the case of std::fs::File::open("doesnotexist.txt") the error type could share a lifetime with the argument though. Admittedly this would make it more difficult to just bubble it up the call stack.
You'd have to .map_err() at lots of call sites to convert to an owned version, which makes it not that much easier to use than using .map_err() at each such site to add the path in either owned or borrowed form. Less error-prone, though (no chance of adding the wrong path).
I think in the vast majority of cases, the overhead of an allocation on error unimportant. It's a tiny optimization that most users won't notice, but it has very visible downsides that users are annoyed by.
(the size of the io::Error type is important since it shows up on every write, but open is relatively rare, and could afford using a slow path to build io::Error).
BTW, File::open already allocates a NUL-terminated and/or UTF-16 version of the path on the happy path. If you want the error to be super efficient, the implementation internals could recycle that allocation.