Inherited C-badness in std::io::Error

This has a few problems:

  • As @ryanavella mentioned in many cases this requires a breaking change or a new function that does the same but with a different error message.
  • Even if that where not an issue: Which one should libraries choose? They would either have to select one of them or be generic over the error type to use. Likely resulting in slower compilation and required opt-in on the library side.
  • At best it solves this specific case of developers wanting different behavior from the stdlib. Think about those that want to avoid std::fmt because of binary size (e.g. for embedded development). They currently need to manually avoid those functions, compile them and let llvm optimize them away.

This might not always be necessary. We (kind of) already have 3 versions of the stdlib: core, alloc and std. Someone building with no_std does not need to recompile the stdlib because a preset of functionalities is already available as a precompiled artifact.

It is implemented like different crates and not one crate with features (not sure if that means that each one has its own precompiled artifact), but I wouldn't be surprised if we end up with multiple precompiled presets of std, which enable different features. For example the one we currently have (focus on performance), one for binary size (no fmt, abort instead of rewind) and one that prefers verbosity over performance (i.e. alloc in errors).

Whether this is practical is a different thing of course, building std might be easier (even though it needs the beta? compiler instead of stable).

The bulk of this discussion has been focused on allocation, but...

(e.g. many C errors of this type get confusing when you do not have permissions to one of their parent directories)

... for this particular subproblem I think the solution would also involve stdlib breaking the path into parts and attempting to open path fds (or some equivalent) for each intermediate directory in turn, right?

That seems like quite a heavy performance cost unrelated to allocation, and might also change the details of how the operation behaves if stdlib doesn't exactly match how the kernel parses paths.

Is there a different way to get information about which path step failed that I'm not thinking of? :thinking:

1 Like

Worse -- it's racy, so it can give the wrong answer if there are concurrent mutations of the file system.

1 Like

But core and alloc are present in std. For targets with std, we only ship std, and even if you build #![no_std], all of std is still there. (That's why extern crate std works, or dependencies that use std, even when the top crate is #![no_std].) The only difference is in what symbols your crate knows how to link against, effectively.

A std profile which puts more information in io::Errors would require shipping a complete separate build of the stdlib, or at least duplicating all symbols that transitively do IO somewhere internally. It's doable, but far from ideal.

Bootstrap was reordered somewhat recently. rustc-stable-N is built from the -beta-N-1 toolchain (with a special magic environment variable to say “this is a toolchain build, allow #![feature] please”, and then rebuilt from the -stable-N toolchain). std-stable-N is now always built with the -stable-N compiler (with that same special environment flag enabling unstable nightly features).

So build-std "works" today without needing any separate toolchains present. The difficult part comes more from the stdlib using a custom build process; either Cargo needs to learn to orchestrate that build directly, or stdlib needs to reduce its usage of the custom build environment and use the same Cargo environment the rest of the ecosystem uses. The proper solution is likely some of both.