traits
import std::io::traits; · source
Read
type trait Read {
read(mut &this, buffer: u8*, length: u64) -> Result<u64, IoError>;
read_all(mut &this, buffer: u8*, length: u64) -> Result<u64, IoError>;
read_byte(mut &this) -> Result<Option<u8>, IoError>;
read_char(mut &this) -> Result<Option<u32>, IoError>;
read_until(mut &this, delimiter: u8, out: mut &Array<u8>) -> Result<u64, IoError>;
read_exact(mut &this, buffer: u8*, length: u64) -> Result<(), IoError>;
read_to_end(mut &this, out: mut &Array<u8>) -> Result<u64, IoError>;
read_to_string(mut &this, out: mut &String) -> Result<u64, IoError>;
read_line(mut &this, out: mut &String) -> Result<u64, IoError>;
}
read is the only required method. It fills up to length bytes and returns how many it actually got. Zero means EOF. A value less than length means the source had nothing more available at this moment — not necessarily EOF, and it is the caller's decision whether to retry.
Everything else is a default built on that one method.
| Method | Behaviour |
|---|---|
read_all(buffer, length) | Read until the buffer is full or EOF. Retries on Interrupted. |
read_exact(buffer, length) | Read exactly length bytes; a short read at EOF is UnexpectedEof. |
read_byte() | None is EOF. |
read_char() | One UTF-8 scalar. |
read_until(delimiter, out) | Reads through the delimiter, which is included in out. |
read_line(out) | Reads through \n, which is included. |
read_to_end(out) | Every remaining byte. |
read_to_string(out) | Every remaining byte as UTF-8. |
read_char fails with UnexpectedEof on a partial scalar and InvalidData on a malformed leading byte, a malformed continuation, or a surrogate — surrogates are not valid Unicode scalars.
Two things worth knowing. read_to_end is unbounded: a pathological source like /dev/zero will exhaust memory, so cap it yourself if the input is untrusted. And read_to_string does not validate UTF-8 — you are certifying that the source is well-formed, or accepting invalid continuation bytes inside a String.
read_until and read_line are byte-level, running read(..., 1) so the stop point is exact. On a source where a one-byte read is expensive — a socket, a file — wrap it in a BufReader, whose read_line override is the fast path.
Implementors
implement trait Read for struct File // std::fs::file
implement<R> trait Read for struct BufReader<R>
where R: Read // std::io::buf
implement trait Read for struct Cursor // std::io::cursor
implement trait Read for struct Stdin // std::io::stdio
implement trait Read for struct StdinLock // std::io::stdio
implement trait Read for struct ChildStdout // std::process::child
implement trait Read for struct ChildStderr // std::process::child
Write
type trait Write {
write_some(mut &this, bytes: Slice<u8>) -> Result<u64, IoError>;
flush(mut &this) -> Result<(), IoError>;
write<T>(mut &this, data: T) -> Result<(), IoError>;
}
write_some is the low-level primitive: write up to bytes.length() bytes and report how many actually landed, which may be fewer on a slow sink. Unbuffered writers override flush with a no-op.
write<T>(data) is the one you normally call. It delivers every byte, retrying short writes and Interrupted, and dispatches on the payload at compile time: a raw Slice<u8>, a Str, a NUL-terminated string, or a single u8. If the sink repeatedly refuses to make progress it fails with WriteZero rather than spinning.
Implementors
implement trait Write for struct File // std::fs::file
implement<W> trait Write for struct BufWriter<W>
where W: Write // std::io::buf
implement<W> trait Write for struct LineWriter<W>
where W: Write // std::io::buf
implement trait Write for struct Cursor // std::io::cursor
implement trait Write for struct Stdout // std::io::stdio
implement trait Write for struct Stderr // std::io::stdio
implement trait Write for struct StdoutLock // std::io::stdio
implement trait Write for struct StderrLock // std::io::stdio
implement trait Write for struct ChildStdin // std::process::child
Seek
type trait Seek {
seek(mut &this, from: SeekFrom) -> Result<u64, IoError>;
stream_position(mut &this) -> Result<u64, IoError>;
rewind(mut &this) -> Result<(), IoError>;
}
seek repositions the cursor and returns the resulting absolute offset. stream_position() reports the current offset without moving, defaulting to seek(Current(0)); rewind() is seek(Start(0)).
Seeking past the end is permitted, and what happens next is sink-specific: a File leaves a sparse gap the OS zero-fills, while a Cursor zero-fills on the next write. Seeking to a negative absolute offset fails with InvalidInput.
SeekFrom
type enum SeekFrom {
Start(u64);
Current(i64);
End(i64);
}
Implementors
implement trait Seek for struct File // std::fs::file
implement trait Seek for struct Cursor // std::io::cursor
The async traits
The non-blocking counterparts of Read and Write, shaped for a buffered connection rather than a bare byte stream.
AsyncRead
type trait AsyncRead {
async fill(mut &this) -> Result<u64, IoError>;
buffered(&this) -> Slice<u8>;
consume(mut &this, count: u64) -> void;
scan_for(&this, delimiter: u8, from: u64) -> u64;
take_front(mut &this, count: u64) -> Result<Array<u8>, IoError>;
async ensure(mut &this, count: u64) -> Result<(), IoError>;
async read_exact(mut &this, count: u64) -> Result<Array<u8>, IoError>;
async read_some(mut &this, limit: u64) -> Result<Array<u8>, IoError>;
async read_until(mut &this, delimiter: u8, limit: u64) -> Result<Array<u8>, IoError>;
async read_line(mut &this, limit: u64) -> Result<String, IoError>;
async skip(mut &this, count: u64) -> Result<(), IoError>;
}
Where Read hands you bytes, AsyncRead hands you a view into its own buffer: fill pulls more from the transport (suspending rather than blocking; zero means the peer closed), buffered() is what has arrived and not yet been consumed, and consume(n) releases the front of it. Every default — read_until, read_line, ensure, read_exact — is a scan over that buffer. The slice from buffered() is valid only until the next fill or consume; do not hold it across an await.
read_until and read_line take a limit so a peer that never sends the delimiter cannot drive the buffer to exhaust memory — exceeding it is InvalidData. read_line strips the terminator, and a \r before it, so CRLF and LF framing read alike.
AsyncWrite
type trait AsyncWrite {
pending(mut &this) -> Array<u8>*;
async flush(mut &this) -> Result<(), IoError>;
queue<T>(mut &this, data: T) -> Result<(), IoError>;
async send<T>(mut &this, data: T) -> Result<(), IoError>;
}
AsyncWrite exposes its outgoing buffer through pending() so an encoder can build a frame in place. queue appends without sending; flush puts everything on the wire, leaving the unsent bytes queued on failure so a retry still has them; send is queue then flush.
AsyncTransport
type trait AsyncTransport {
async read_into(mut &this, buf: Array<u8>) -> Transfer;
async write_from(mut &this, buf: Array<u8>) -> Transfer;
}
type struct Transfer {
buf: Array<u8>;
result: Result<u64, IoError>;
take_buf(mut &this) -> Array<u8>;
outcome(&this) -> Result<u64, IoError>;
}
AsyncTransport is the seam underneath both: two operations that move an owned buffer to the transport and back through a Transfer. This is what lets a plaintext TcpStream and a TlsStream share one BufStream implementation — everything above the seam is written once.
Implementors
implement trait AsyncRead for struct BufStream<S> // std::io::buf
implement trait AsyncWrite for struct BufStream<S> // std::io::buf
implement trait AsyncTransport for struct TcpStream // std::net::socket::tcp
implement trait AsyncTransport for struct TlsStream // std::net::tls::future