Conversation

To make manageable #Rust stacks over time, I’ve ended up to following conclusions on how to make I/O connectors between library crates:

  1. Internal Read, Seek and Write traits that specify an API that is a more constrained versions of std::io counterparts. Here the masking is used for benefit so that all in-crate code depends on exactly to internal traits.
  2. A submodule named std.rs containing std::io implementations and macro shenanigans to use it [1]. This way std can be compiled out if required (e.g. when used with embedded_io).
  3. Optionally outside I/O can be embedded with a tuple or enum value containing a tuple.

This way my own crates are pretty easy to glue to std, embedded_io and other crates providing the I/O backend so this sort of indirection makes a lot of sense. The goal is to have a structure with clean separation of internals and connectors to the outside world (which can or might not include std).

[1]

#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")] 
mod std;

#rustlang

1
0
0

If the masking gets in the way (which it rarely actually does because compiler is smart), some corner cases can be sorted by calling convention e.g. std::io::Read::read(self, buf). This happens super rarely in practice.

0
0
0