Skip to content

Commit 27be7bc

Browse files
authored
Merge pull request #469 from rust-embedded/io-docs
io: expand docs.
2 parents ae327b8 + b55faf9 commit 27be7bc

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.
@@ -207,12 +207,33 @@ impl<E: fmt::Debug> std::error::Error for WriteAllError<E> {}
207207

208208
/// Blocking reader.
209209
///
210-
/// Semantics are the same as [`std::io::Read`], check its documentation for details.
210+
/// This trait is the `embedded-io` equivalent of [`std::io::Read`].
211211
pub trait Read: crate::ErrorType {
212-
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
212+
/// Read some bytes from this source into the specified buffer, returning how many bytes were read.
213+
///
214+
/// If no bytes are currently available to read, this function blocks until at least one byte is available.
215+
///
216+
/// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount
217+
/// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the
218+
/// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately
219+
/// available.
220+
///
221+
/// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF
222+
/// will always be so in the future, for example a reader can stop being at EOF if another process appends
223+
/// more bytes to the underlying file.
224+
///
225+
/// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error.
226+
/// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer.
213227
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
214228

215229
/// Read the exact number of bytes required to fill `buf`.
230+
///
231+
/// This function calls `read()` in a loop until exactly `buf.len()` bytes have
232+
/// been read, blocking if needed.
233+
///
234+
/// If you are using [`ReadReady`] to avoid blocking, you should not use this function.
235+
/// `ReadReady::read_ready()` returning true only guarantees the first call to `read()` will
236+
/// not block, so this function may still block in subsequent calls.
216237
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError<Self::Error>> {
217238
while !buf.is_empty() {
218239
match self.read(buf) {
@@ -231,9 +252,15 @@ pub trait Read: crate::ErrorType {
231252

232253
/// Blocking buffered reader.
233254
///
234-
/// Semantics are the same as [`std::io::BufRead`], check its documentation for details.
255+
/// This trait is the `embedded-io` equivalent of [`std::io::BufRead`].
235256
pub trait BufRead: crate::ErrorType {
236257
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
258+
///
259+
/// If no bytes are currently available to read, this function blocks until at least one byte is available.
260+
///
261+
/// If the reader is at end-of-file (EOF), an empty slice is returned. There is no guarantee that a reader at EOF
262+
/// will always be so in the future, for example a reader can stop being at EOF if another process appends
263+
/// more bytes to the underlying file.
237264
fn fill_buf(&mut self) -> Result<&[u8], Self::Error>;
238265

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

243270
/// Blocking writer.
244271
///
245-
/// Semantics are the same as [`std::io::Write`], check its documentation for details.
272+
/// This trait is the `embedded-io` equivalent of [`std::io::Write`].
246273
pub trait Write: crate::ErrorType {
247274
/// Write a buffer into this writer, returning how many bytes were written.
275+
///
276+
/// If the writer is not currently ready to accept more bytes (for example, its buffer is full),
277+
/// this function blocks until it is ready to accept least one byte.
278+
///
279+
/// If it's ready to accept bytes, a non-zero amount of bytes is written from the beginning of `buf`, and the amount
280+
/// is returned. It is not guaranteed that *all* available buffer space is filled, i.e. it is possible for the
281+
/// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be
282+
/// ready to accept more bytes immediately.
283+
///
284+
/// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not
285+
/// able to accept more bytes and likely never will are better indicated with errors.
286+
///
287+
/// If `buf.len() == 0`, `write` returns without blocking, with either `Ok(0)` or an error.
288+
/// The `Ok(0)` doesn't indicate an error.
248289
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;
249290

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

253294
/// Write an entire buffer into this writer.
295+
///
296+
/// This function calls `write()` in a loop until exactly `buf.len()` bytes have
297+
/// been written, blocking if needed.
298+
///
299+
/// If you are using [`WriteReady`] to avoid blocking, you should not use this function.
300+
/// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will
301+
/// not block, so this function may still block in subsequent calls.
254302
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError<Self::Error>> {
255303
while !buf.is_empty() {
256304
match self.write(buf) {
@@ -263,6 +311,13 @@ pub trait Write: crate::ErrorType {
263311
}
264312

265313
/// Write a formatted string into this writer, returning any error encountered.
314+
///
315+
/// This function calls `write()` in a loop until the entire formatted string has
316+
/// been written, blocking if needed.
317+
///
318+
/// If you are using [`WriteReady`] to avoid blocking, you should not use this function.
319+
/// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will
320+
/// not block, so this function may still block in subsequent calls.
266321
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError<Self::Error>> {
267322
// Create a shim which translates a Write to a fmt::Write and saves
268323
// off I/O errors. instead of discarding them
@@ -303,7 +358,7 @@ pub trait Write: crate::ErrorType {
303358

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

0 commit comments

Comments
 (0)