Skip to content

Commit b55faf9

Browse files
committed
io: expand docs.
1 parent 64b66b5 commit b55faf9

File tree

3 files changed

+137
-14
lines changed

3 files changed

+137
-14
lines changed

embedded-io-async/src/lib.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,41 @@ pub use embedded_io::{
1515

1616
/// Async reader.
1717
///
18-
/// Semantics are the same as [`std::io::Read`], check its documentation for details.
18+
/// This trait is the `embedded-io-async` equivalent of [`std::io::Read`].
1919
pub trait Read: ErrorType {
20-
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
20+
/// Read some bytes from this source into the specified buffer, returning how many bytes were read.
21+
///
22+
/// If no bytes are currently available to read, this function waits until at least one byte is available.
23+
///
24+
/// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount
25+
/// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the
26+
/// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately
27+
/// available.
28+
///
29+
/// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF
30+
/// will always be so in the future, for example a reader can stop being at EOF if another process appends
31+
/// more bytes to the underlying file.
32+
///
33+
/// If `buf.len() == 0`, `read` returns without waiting, with either `Ok(0)` or an error.
34+
/// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer.
35+
///
36+
/// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e.
37+
/// guarantee that if you cancel (drop) a `read()` future that hasn't completed yet, the stream's
38+
/// state hasn't changed (no bytes have been read).
39+
///
40+
/// This is not a requirement to allow implementations that read into the user's buffer straight from
41+
/// the hardware with e.g. DMA.
42+
///
43+
/// Implementations should document whether they're actually side-effect-free on cancel or not.
2144
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
2245

2346
/// Read the exact number of bytes required to fill `buf`.
47+
///
48+
/// This function calls `read()` in a loop until exactly `buf.len()` bytes have
49+
/// been read, waiting if needed.
50+
///
51+
/// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned
52+
/// future that hasn't completed yet, some bytes might have already been read, which will get lost.
2453
async fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError<Self::Error>> {
2554
while !buf.is_empty() {
2655
match self.read(buf).await {
@@ -39,9 +68,15 @@ pub trait Read: ErrorType {
3968

4069
/// Async buffered reader.
4170
///
42-
/// Semantics are the same as [`std::io::BufRead`], check its documentation for details.
71+
/// This trait is the `embedded-io-async` equivalent of [`std::io::BufRead`].
4372
pub trait BufRead: ErrorType {
4473
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
74+
///
75+
/// If no bytes are currently available to read, this function waits until at least one byte is available.
76+
///
77+
/// If the reader is at end-of-file (EOF), an empty slice is returned. There is no guarantee that a reader at EOF
78+
/// will always be so in the future, for example a reader can stop being at EOF if another process appends
79+
/// more bytes to the underlying file.
4580
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error>;
4681

4782
/// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
@@ -50,9 +85,32 @@ pub trait BufRead: ErrorType {
5085

5186
/// Async writer.
5287
///
53-
/// Semantics are the same as [`std::io::Write`], check its documentation for details.
88+
/// This trait is the `embedded-io-async` equivalent of [`std::io::Write`].
5489
pub trait Write: ErrorType {
5590
/// Write a buffer into this writer, returning how many bytes were written.
91+
///
92+
/// If the writer is not currently ready to accept more bytes (for example, its buffer is full),
93+
/// this function waits until it is ready to accept least one byte.
94+
///
95+
/// If it's ready to accept bytes, a non-zero amount of bytes is written from the beginning of `buf`, and the amount
96+
/// is returned. It is not guaranteed that *all* available buffer space is filled, i.e. it is possible for the
97+
/// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be
98+
/// ready to accept more bytes immediately.
99+
///
100+
/// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not
101+
/// able to accept more bytes and likely never will are better indicated with errors.
102+
///
103+
/// If `buf.len() == 0`, `write` returns without waiting, with either `Ok(0)` or an error.
104+
/// The `Ok(0)` doesn't indicate an error.
105+
///
106+
/// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e.
107+
/// guarantee that if you cancel (drop) a `write()` future that hasn't completed yet, the stream's
108+
/// state hasn't changed (no bytes have been written).
109+
///
110+
/// This is not a requirement to allow implementations that write from the user's buffer straight to
111+
/// the hardware with e.g. DMA.
112+
///
113+
/// Implementations should document whether they're actually side-effect-free on cancel or not.
56114
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;
57115

58116
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
@@ -61,6 +119,12 @@ pub trait Write: ErrorType {
61119
}
62120

63121
/// Write an entire buffer into this writer.
122+
///
123+
/// This function calls `write()` in a loop until exactly `buf.len()` bytes have
124+
/// been written, waiting if needed.
125+
///
126+
/// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned
127+
/// future that hasn't completed yet, some bytes might have already been written.
64128
async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteAllError<Self::Error>> {
65129
let mut buf = buf;
66130
while !buf.is_empty() {
@@ -76,7 +140,7 @@ pub trait Write: ErrorType {
76140

77141
/// Async seek within streams.
78142
///
79-
/// Semantics are the same as [`std::io::Seek`], check its documentation for details.
143+
/// This trait is the `embedded-io-async` equivalent of [`std::io::Seek`].
80144
pub trait Seek: ErrorType {
81145
/// Seek to an offset, in bytes, in a stream.
82146
async fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error>;

embedded-io/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ Rust's `std::io` traits are not available in `no_std` targets, mainly because `s
1212
requires allocation. This crate contains replacement equivalent traits, usable in `no_std`
1313
targets.
1414

15-
The only difference with `std::io` is `Error` is an associated type. This allows each implementor
16-
to return its own error type, while avoiding `dyn` or `Box`. This is how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/).
15+
## Differences with `std::io`
16+
17+
- `Error` is an associated type. This allows each implementor to return its own error type,
18+
while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/).
19+
- In `std::io`, the `Read`/`Write` traits might be blocking or non-blocking (i.e. returning `WouldBlock` errors) depending on the file descriptor's mode, which is only known at run-time. This allows passing a non-blocking stream to code that expects a blocking
20+
stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way.
1721

1822
## Minimum Supported Rust Version (MSRV)
1923

embedded-io/src/lib.rs

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod impls;
1515

1616
/// Enumeration of possible methods to seek within an I/O object.
1717
///
18-
/// Semantics are the same as [`std::io::SeekFrom`], check its documentation for details.
18+
/// This is the `embedded-io` equivalent of [`std::io::SeekFrom`].
1919
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
2020
pub enum SeekFrom {
2121
/// Sets the offset to the provided number of bytes.
@@ -149,12 +149,33 @@ impl<E: fmt::Debug> std::error::Error for WriteAllError<E> {}
149149

150150
/// Blocking reader.
151151
///
152-
/// Semantics are the same as [`std::io::Read`], check its documentation for details.
152+
/// This trait is the `embedded-io` equivalent of [`std::io::Read`].
153153
pub trait Read: crate::ErrorType {
154-
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
154+
/// Read some bytes from this source into the specified buffer, returning how many bytes were read.
155+
///
156+
/// If no bytes are currently available to read, this function blocks until at least one byte is available.
157+
///
158+
/// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount
159+
/// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the
160+
/// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately
161+
/// available.
162+
///
163+
/// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF
164+
/// will always be so in the future, for example a reader can stop being at EOF if another process appends
165+
/// more bytes to the underlying file.
166+
///
167+
/// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error.
168+
/// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer.
155169
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
156170

157171
/// Read the exact number of bytes required to fill `buf`.
172+
///
173+
/// This function calls `read()` in a loop until exactly `buf.len()` bytes have
174+
/// been read, blocking if needed.
175+
///
176+
/// If you are using [`ReadReady`] to avoid blocking, you should not use this function.
177+
/// `ReadReady::read_ready()` returning true only guarantees the first call to `read()` will
178+
/// not block, so this function may still block in subsequent calls.
158179
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError<Self::Error>> {
159180
while !buf.is_empty() {
160181
match self.read(buf) {
@@ -173,9 +194,15 @@ pub trait Read: crate::ErrorType {
173194

174195
/// Blocking buffered reader.
175196
///
176-
/// Semantics are the same as [`std::io::BufRead`], check its documentation for details.
197+
/// This trait is the `embedded-io` equivalent of [`std::io::BufRead`].
177198
pub trait BufRead: crate::ErrorType {
178199
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
200+
///
201+
/// If no bytes are currently available to read, this function blocks until at least one byte is available.
202+
///
203+
/// If the reader is at end-of-file (EOF), an empty slice is returned. There is no guarantee that a reader at EOF
204+
/// will always be so in the future, for example a reader can stop being at EOF if another process appends
205+
/// more bytes to the underlying file.
179206
fn fill_buf(&mut self) -> Result<&[u8], Self::Error>;
180207

181208
/// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
@@ -184,15 +211,36 @@ pub trait BufRead: crate::ErrorType {
184211

185212
/// Blocking writer.
186213
///
187-
/// Semantics are the same as [`std::io::Write`], check its documentation for details.
214+
/// This trait is the `embedded-io` equivalent of [`std::io::Write`].
188215
pub trait Write: crate::ErrorType {
189216
/// Write a buffer into this writer, returning how many bytes were written.
217+
///
218+
/// If the writer is not currently ready to accept more bytes (for example, its buffer is full),
219+
/// this function blocks until it is ready to accept least one byte.
220+
///
221+
/// If it's ready to accept bytes, a non-zero amount of bytes is written from the beginning of `buf`, and the amount
222+
/// is returned. It is not guaranteed that *all* available buffer space is filled, i.e. it is possible for the
223+
/// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be
224+
/// ready to accept more bytes immediately.
225+
///
226+
/// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not
227+
/// able to accept more bytes and likely never will are better indicated with errors.
228+
///
229+
/// If `buf.len() == 0`, `write` returns without blocking, with either `Ok(0)` or an error.
230+
/// The `Ok(0)` doesn't indicate an error.
190231
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;
191232

192-
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
233+
/// Flush this output stream, blocking until all intermediately buffered contents reach their destination.
193234
fn flush(&mut self) -> Result<(), Self::Error>;
194235

195236
/// Write an entire buffer into this writer.
237+
///
238+
/// This function calls `write()` in a loop until exactly `buf.len()` bytes have
239+
/// been written, blocking if needed.
240+
///
241+
/// If you are using [`WriteReady`] to avoid blocking, you should not use this function.
242+
/// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will
243+
/// not block, so this function may still block in subsequent calls.
196244
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError<Self::Error>> {
197245
while !buf.is_empty() {
198246
match self.write(buf) {
@@ -205,6 +253,13 @@ pub trait Write: crate::ErrorType {
205253
}
206254

207255
/// Write a formatted string into this writer, returning any error encountered.
256+
///
257+
/// This function calls `write()` in a loop until the entire formatted string has
258+
/// been written, blocking if needed.
259+
///
260+
/// If you are using [`WriteReady`] to avoid blocking, you should not use this function.
261+
/// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will
262+
/// not block, so this function may still block in subsequent calls.
208263
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError<Self::Error>> {
209264
// Create a shim which translates a Write to a fmt::Write and saves
210265
// off I/O errors. instead of discarding them
@@ -245,7 +300,7 @@ pub trait Write: crate::ErrorType {
245300

246301
/// Blocking seek within streams.
247302
///
248-
/// Semantics are the same as [`std::io::Seek`], check its documentation for details.
303+
/// This trait is the `embedded-io` equivalent of [`std::io::Seek`].
249304
pub trait Seek: crate::ErrorType {
250305
/// Seek to an offset, in bytes, in a stream.
251306
fn seek(&mut self, pos: crate::SeekFrom) -> Result<u64, Self::Error>;

0 commit comments

Comments
 (0)