# A plan to move most of \`std::io\` to \`alloc\`

**URL:** https://internals.rust-lang.org/t/a-plan-to-move-most-of-std-io-to-alloc/23678
**Category:** libs
**Created:** [November 3, 2025, 11:50am UTC](https://internals.rust-lang.org/t/a-plan-to-move-most-of-std-io-to-alloc/23678 "2025-11-03T11:50:57Z")
**Posts on this page:** 1
**Showing post:** 9

<div class="post-metadata">

### Author: ![a1phyr](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/a1phyr/32/7229_2.png) [@a1phyr](https://internals.rust-lang.org/u/a1phyr)
#### Post date: [November 27, 2025, 3:19pm UTC](https://internals.rust-lang.org/t/a-plan-to-move-most-of-std-io-to-alloc/23678/9 "2025-11-27T15:19:40Z")

</div>

Thanks everyone for your replies!

> [@bjorn3](#):
>
> Default impls are the hardest part of EII to implement. Several targets don't support weak symbols at all or have rather buggy support. This includes the windows-gnu targets where weak symbols are non-functional and windows-msvc where exporting weak symbols from dylibs requires a hacky implementation.

I am surprised to hear that. Doesn't `#[global_allocator]` work on these platforms as well? What prevent using the same technique with EII?

> [@josh](#):
>
> I think we could manage to move a fair bit of `io` to `alloc` or even to `core`, by relying on the fact that all of `core`/`alloc`/`std` are one coherence domain.

This is interesting to know indeed. While splitting the definition of `io::Error` would be complex, splitting the methods could enable something like "manual EII" (where constructing an `Error` from raw OS error would require providing the necessary functions).

> [@scottmcm](#):
>
> I also wonder how much most of the code involved in these _traits_ really care what the error types are? For example, could we have a `GenericRead` with an associated `Error` type, so that std's `Read` is just `trait Read : super GenericRead<Error = io::Error> {}`?

I don't think so. Adding a supertrait to `Read` would be a breaking change, and blanket impls from `GenericRead<io::Error>` to `Read` would be complex with the exiting `impl<R> Read for &mut R`. I think that it would require something like specialization lattice?

Also code using these traits often rely on the fact that they can create a custom `io::Error`. Just in `std`, you have instances of this in `Read::read_exact`, `Read::read_to_string`, `Write::write_all`, plus OOM handling in `Read::read_to_end` (and probably more). And it would be nice to keep having these specific ones constants.

Additionally, I fear that creating new traits would split the ecosystem.

Overall, I think that doing the work to migrate the existing traits is easier and better than working around it.

> [@kornel](#):
>
> The `std::error::Error` [got split to be possible to expose it in `core`](https://github.com/rust-lang/rust/issues/103765). Maybe `io::Read`/`io::Write` could get a similar treatment?

Note that this is the `impl` block of the **type** `dyn std::error::Error` that is split, not the definition of the **trait**. That is why my proposal is only to migrate to `alloc` for now.

---

_[View the full topic](https://internals.rust-lang.org/t/a-plan-to-move-most-of-std-io-to-alloc/23678)._
