Skip to content

add split traits to fill functionality gaps in downstream crates - #748

Open
celogic wants to merge 1 commit into
rust-embedded:masterfrom
celogic:features
Open

add split traits to fill functionality gaps in downstream crates#748
celogic wants to merge 1 commit into
rust-embedded:masterfrom
celogic:features

Conversation

@celogic

@celogic celogic commented Aug 19, 2026

Copy link
Copy Markdown

Adds a SplitRW trait for both async and non async versions.
It abstracts the capability, making them available for downstream libraries.

  • Why the name SplitRW? Split into Reader and Writer
  • Why not just Split? To not be confused with std::io::Split, which is an Iterator
  • SplitRW doesn't require Read + Write, because i don't think its necessary and leaves the implementor more flexible

Example:
embedded_tls::TlsConnection builds on (abstract) <Socket: embedded_io_async::Read + embedded_io_async::Write>.
embedded_tls::TlsConnection impl the fn split, but it requires Socket: Clone.
this is not effective as Socket doesn't need to be completely cloned, which causes functionality gaps.
e.g. if i use embassy_net::tcp::TcpSocket as the underlaying Socket, I cant call TlsConnection::split,
because embassy_net::tcp::TcpSocket doesn't impl Clone, despite providing split itself

@celogic
celogic requested a review from a team as a code owner August 19, 2026 10:18
@MabezDev

Copy link
Copy Markdown
Member

Splitting semantics get tricky, you haven't accounted for anything beyond just splitting (think: configuration that affects both sides etc) . I also don't believe this is a e-hal specific problem so I'm not sure it belongs here.

@Dirbaio

Dirbaio commented Aug 19, 2026

Copy link
Copy Markdown
Member

I got some concerns:

  • std::io doesn't have such a trait. Why? Has it ever been proposed, was it deemed not necessary for some reason? How do libs using std::io deal with this problem? Is embedded really different? IMO we shouldn't add something std::io doesn't have unless we have a good reason.
  • The signature isn't quite right. It takes borrowed self and returns owned halves. It should be either all borrowed or all owned. Or both. Which one we want is not obvious, depending on the case an impl may be able to do borrowed only, but for users owned is often more convenient.
trait BorrowedSplit {
    type Reader<'a>: Read where Self: 'a;
    type Writer<'a>: Write where Self: 'a;
    fn split_rw(&mut self) -> (Self::Reader<'_>, Self::Writer<'_>);
}
trait OwnedSplit {
    type Reader: Read;
    type Writer: Write;
    fn split_rw(self) -> (Self::Reader, Self::Writer);
}
  • Should it constrain the error types for the children to be the same? Maybe not since a lib that wants that can always do it with where clauses I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants