Module std::rt::io

Synchronous I/O. Synchronous I/O. Synchronous I/O

This module defines the Rust interface for synchronous I/O. It models byte-oriented input and output with the Reader and Writer traits. Types that implement both Reader and Writer and called 'streams', and automatically implement trait Stream. Implementations are provided for common I/O streams like file, TCP, UDP, Unix domain sockets. Readers and Writers may be composed to add capabilities like string parsing, encoding, and compression.

This will likely live in std::io, not std::rt::io.

Examples

Some examples of obvious things you might want to do

Terms

Blocking and synchrony

When discussing I/O you often hear the terms 'synchronous' and 'asynchronous', along with 'blocking' and 'non-blocking' compared and contrasted. A synchronous I/O interface performs each I/O operation to completion before proceeding to the next. Synchronous interfaces are usually used in imperative style as a sequence of commands. An asynchronous interface allows multiple I/O requests to be issued simultaneously, without waiting for each to complete before proceeding to the next.

Asynchronous interfaces are used to achieve 'non-blocking' I/O. In traditional single-threaded systems, performing a synchronous I/O operation means that the program stops all activity (it 'blocks') until the I/O is complete. Blocking is bad for performance when there are other computations that could be done.

Asynchronous interfaces are most often associated with the callback (continuation-passing) style popularised by node.js. Such systems rely on all computations being run inside an event loop which maintains a list of all pending I/O events; when one completes the registered callback is run and the code that made the I/O request continues. Such interfaces achieve non-blocking at the expense of being more difficult to reason about.

Rust's I/O interface is synchronous - easy to read - and non-blocking by default.

Remember that Rust tasks are 'green threads', lightweight threads that are multiplexed onto a single operating system thread. If that system thread blocks then no other task may proceed. Rust tasks are relatively cheap to create, so as long as other tasks are free to execute then non-blocking code may be written by simply creating a new task.

When discussing blocking in regards to Rust's I/O model, we are concerned with whether performing I/O blocks other Rust tasks from proceeding. In other words, when a task calls read, it must then wait (or 'sleep', or 'block') until the call to read is complete. During this time, other tasks may or may not be executed, depending on how read is implemented.

Rust's default I/O implementation is non-blocking; by cooperating directly with the task scheduler it arranges to never block progress of other tasks. Under the hood, Rust uses asynchronous I/O via a per-scheduler (and hence per-thread) event loop. Synchronous I/O requests are implemented by descheduling the running task and performing an asynchronous request; the task is only resumed once the asynchronous request completes.

For blocking (but possibly more efficient) implementations, look in the io::native module.

Error Handling

I/O is an area where nearly every operation can result in unexpected errors. It should allow errors to be handled efficiently. It needs to be convenient to use I/O when you don't care about dealing with specific errors.

Rust's I/O employs a combination of techniques to reduce boilerplate while still providing feedback about errors. The basic strategy:

These features combine in the API to allow for expressions like File::new("diary.txt").write_line("met a girl") without having to worry about whether "diary.txt" exists or whether the write succeeds. As written, if either new or write_line encounters an error the task will fail.

If you wanted to handle the error though you might write

let mut error = None;
do io_error::cond(|e: IoError| {
    error = Some(e);
}).in {
    File::new("diary.txt").write_line("met a girl");
}

if error.is_some() {
    println("failed to write my diary");
}

XXX: Need better condition handling syntax

In this case the condition handler will have the opportunity to inspect the IoError raised by either the call to new or the call to write_line, but then execution will continue.

So what actually happens if new encounters an error? To understand that it's important to know that what new returns is not a File but an Option<File>. If the file does not open, and the condition is handled, then new will simply return None. Because there is an implementation of Writer (the trait required ultimately required for types to implement write_line) there is no need to inspect or unwrap the Option<File> and we simply call write_line on it. If new returned a None then the followup call to write_line will also raise an error.

Concerns about this strategy

This structure will encourage a programming style that is prone to errors similar to null pointer dereferences. In particular code written to ignore errors and expect conditions to be unhandled will start passing around null or zero objects when wrapped in a condition handler.

Issues with i/o scheduler affinity, work stealing, task pinning

Resource management

Paths, URLs and overloaded constructors

Scope

In scope for core

Some I/O things don't belong in core

Out of scope

XXX Questions and issues

Reexports

pub use self::stdio::stdin;
pub use self::stdio::stdout;
pub use self::file::FileStream;
pub use self::timer::Timer;
pub use self::extensions::ReaderUtil;
pub use self::net::ip::IpAddr;
pub use self::extensions::WriterByteConversions;
pub use self::pipe::UnboundPipeStream;
pub use self::net::tcp::TcpListener;
pub use self::stdio::stderr;
pub use self::net::tcp::TcpStream;
pub use self::stdio::println;
pub use self::net::udp::UdpStream;
pub use self::extensions::ReaderByteConversions;
pub use self::pipe::PipeStream;
pub use self::process::Process;
pub use self::stdio::print;

Modules

buffered

Buffered I/O wrappers Buffered I/O wrappers Buffering wrappers for I/O traits

comm_adapters

Interop between byte streams and pipes. Not sure where it belongs Interop between byte streams and pipes. Not sure where it belongs

extensions

Extension traits Extension traits Utility mixins that apply to all Readers and Writers

file

Synchronous, non-blocking file I/O. Synchronous, non-blocking file I/O. Synchronous File I/O

flate

Basic stream compression. XXX: Belongs with other flate code Basic stream compression. XXX: Belongs with other flate code Some various other I/O types

io_error
mem

Readers and Writers for memory buffers and strings. Readers and Writers for memory buffers and strings. Readers and Writers for in-memory buffers

mock

Mock implementations for testing Mock implementations for testing

native

Thread-blocking implementations

net

Synchronous, non-blocking network I/O. Synchronous, non-blocking network I/O.

option

Implementations for Option Implementations for Option Implementations of I/O traits for the Option type

pipe

Synchronous, in-memory I/O. Synchronous, in-memory I/O. Synchronous, in-memory pipes.

process

Child process management. Child process management. Bindings for executing child processes

read_error
stdio

Non-blocking access to stdin, stdout, stderr Non-blocking access to stdin, stdout, stderr

support

Non-I/O things needed by the I/O module Non-I/O things needed by the I/O module

timer

Basic Timer Basic Timer

Structs

FileStat
IoError

The type passed to I/O condition handlers to indicate error

Enums

FileAccess

Access permissions with which the file should be opened. FileStreams opened with Read will raise an io_error condition if written to.

FileMode

Instructions on how to open a file and return a FileStream.

IoErrorKind
SeekStyle

Statics

pub static DEFAULT_BUF_SIZE: uint = 1024 * 64

The default buffer size for various I/O operations XXX: Not pub

 

Traits

Acceptor

An acceptor is a value that presents incoming connections

Decorator

Common trait for decorator types.

Listener

A listener is a value that can consume itself to start listening for connections. Doing so produces some sort of Acceptor.

Reader
Seek

XXX

  • Are u64 and i64 the right choices?
Stream
Writer

Functions

ignore_io_error

Helper for wrapper calls where you want to ignore any io_errors that might be raised

placeholder_error
standard_error