[rust-dev] Update on I/O progress

Brian Anderson banderson at mozilla.com
Wed Apr 24 15:38:32 PDT 2013


Hi.

As you may know, I'm in the process of redesigning the I/O module on top 
of a new task scheduler. With my [latest pull request] I've built enough 
infrastructure that I'm about ready to start implementing TCP streams. 
Before I get too deep though I want to make sure that everybody who 
cares is aware of the design.

[latest pull request]: https://github.com/mozilla/rust/pull/6046

I had a grand idea that I would be able to produce a full [design doc], 
but that's not going to happen I think. What I do have is a fair bit of 
[API documentation], along with a growing list of [unresolved issues], 
some of which I'll present here.

[design doc]: https://github.com/mozilla/rust/wiki/Lib-io
[API documentation]: 
https://github.com/brson/rust/blob/io/src/libcore/rt/io/mod.rs#L11
[unresolved issues]: 
https://github.com/brson/rust/blob/io/src/libcore/rt/io/mod.rs#L220

# Scope

What I am focused on now is the design of a synchronous API for sending 
and receiving streams of bytes, along with an implementation built on an 
internal *asynchronous* I/O interface attached to the scheduler event 
loop. This includes reading and writing files, TCP, UDP, Unix sockets, 
stdio, pipes, adapters for string parsing, compression, memory buffers, etc.

There are plenty of important and related issues that I have not 
considered yet, such as file and directory traversal, application-level 
protocols like HTTP, async I/O. We need to take important use cases into 
account, but don't necessarily need to implement them now.

I am particularly interested in making sure that common operations are 
convenient and don't require a lot of boilerplate. To that end I've been 
collecting [aspirational examples] of succinctly written Rust I/O code 
using the hypothetical API. The goal will be to turn these into test 
cases. I welcome additional examples.

[aspirational examples]: 
https://github.com/brson/rust/blob/io/src/libcore/rt/io/mod.rs#L24

# Overview

The API I am proposing has a structure based on what we have now, with a 
Reader and Writer trait that define a few primitive operations, along 
with a number of types that implement them. Types that implement Reader 
and Writer are called 'streams' and automatically implement `Stream`. 
The types are influenced primarily by the existing `core::io`, .NET, Go, 
and Ruby.

I recommend reading the previously linked module documentation for the 
complete story.

# Issues

Here I'll discuss some of the issues I've encountered so far.

## Error handling

I've spent a lot of time thinking about - and talking to Patrick about - 
how I/O should deal with errors (the old `io` doesn't have a consistent 
strategy). The [error handling strategy] I'm proposing is intended to 
eliminate boilerplate and allow efficient recovery from errors, but it 
uses some uncommon patterns.

[error handling strategy]: 
https://github.com/brson/rust/blob/io/src/libcore/rt/io/mod.rs#L128

In particular, it forces all return values to be 'nullable' or 
'zeroable', which will lead to the usual logic errors under the right 
circumstances. It also uses conditions, which aren't used much yet in Rust.

## String encoding

I have not yet put much thought about how to deal with string encoding 
and decoding. The existing `io` module simply has Reader and Writer 
extension traits that add a number of methods like `read_str`, etc. I 
think this is the wrong approach because it doesn't allow any extra 
state for the encoding/decoding, e.g. there's no way to customize the 
way newline is handled. Probably we'll need some decorator types like 
`StringReader`, etc.

## Close

Do we need to have `close` methods or do we depend solely on RAII for 
closing streams?

## Constructor options

There tend to be a lot of ways to open I/O types. Some potential examples:

* File::open("diary.txt")
* File::open(Path("diary.txt"))
* File::open(Path("diary.txt"), Create, WriteOnly)
* File::open(Url("file://diary.txt"))

The one you want to put at the front of the tutorial is the simple 
`File::open("diary.txt")`, but then how do you use the other variations? 
`File::open_path(...)`, `File::open_with_options(...)`?

I am considering defining `open` more like `fn open:<S: OpenSpec>(spec: 
S)`, then implementing `OpenSpec` for e.g. `&str`, `Path`, `(Path, 
FileFlags, FileAccess), `Url`, etc.

Does this seem reasonable?

# Closing

Sorry for the barrage of random notes. Again I encourage those 
interested to read the [rt::io module docs] and the rest of the [rt::io 
module] to get a clearer idea of where I'm going with this.

[rt::io module docs]: 
https://github.com/brson/rust/blob/io/src/libcore/rt/io/mod.rs#L11
[rt::io module]: https://github.com/brson/rust/tree/io/src/libcore/rt/io

-Brian







More information about the Rust-dev mailing list