Skip to content

io: code examples, un-xfail conditions tutorial #11271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 42 additions & 36 deletions doc/tutorial-conditions.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ $ ./example numbers.txt

An example program that does this task reads like this:

~~~~{.xfail-test}
~~~~
# #[allow(unused_imports)];
# extern mod extra;
use std::io::buffered::BufferedReader;
use std::io::fs::File;
use std::io::File;
# mod BufferedReader {
# use std::io::fs::File;
# use std::io::File;
# use std::io::mem::MemReader;
# use std::io::buffered::BufferedReader;
# static s : &'static [u8] = bytes!("1 2\n\
Expand All @@ -71,10 +72,9 @@ fn main() {
fn read_int_pairs() -> ~[(int,int)] {
let mut pairs = ~[];

let args = std::os::args();

// Path takes a generic by-value, rather than by reference
let path = Path::new(args.get_opt(1).expect("No input file parameter!").as_slice());
# let _g = std::io::ignore_io_error();
let path = Path::new(&"foo.txt");
let mut reader = BufferedReader::new(File::open(&path));

// 1. Iterate over the lines of our file.
Expand Down Expand Up @@ -242,13 +242,14 @@ If the example is rewritten to use failure, these error cases can be trapped.
In this rewriting, failures are trapped by placing the I/O logic in a sub-task,
and trapping its exit status using `task::try`:

~~~~{.xfail-test}
~~~~
# #[allow(unused_imports)];
# extern mod extra;
use std::io::buffered::BufferedReader;
use std::io::fs::File;
use std::io::File;
use std::task;
# mod BufferedReader {
# use std::io::fs::File;
# use std::io::File;
# use std::io::mem::MemReader;
# use std::io::buffered::BufferedReader;
# static s : &'static [u8] = bytes!("1 2\n\
Expand Down Expand Up @@ -280,8 +281,8 @@ fn main() {

fn read_int_pairs() -> ~[(int,int)] {
let mut pairs = ~[];
let args = std::os::args();
let path = Path::new(args.get_opt(1).expect("No input file parameter!").as_slice());
# let _g = std::io::ignore_io_error();
let path = Path::new(&"foo.txt");

let mut reader = BufferedReader::new(File::open(&path));
for line in reader.lines() {
Expand Down Expand Up @@ -346,12 +347,13 @@ If no handler is found, `Condition::raise` will fail the task with an appropriat
Rewriting the example to use a condition in place of ignoring malformed lines makes it slightly longer,
but similarly clear as the version that used `fail!` in the logic where the error occurs:

~~~~{.xfail-test}
~~~~
# #[allow(unused_imports)];
# extern mod extra;
use std::io::buffered::BufferedReader;
use std::io::fs::File;
use std::io::File;
# mod BufferedReader {
# use std::io::fs::File;
# use std::io::File;
# use std::io::mem::MemReader;
# use std::io::buffered::BufferedReader;
# static s : &'static [u8] = bytes!("1 2\n\
Expand All @@ -378,8 +380,8 @@ fn main() {

fn read_int_pairs() -> ~[(int,int)] {
let mut pairs = ~[];
let args = std::os::args();
let path = Path::new(args.get_opt(1).expect("No input file parameter!").as_slice());
# let _g = std::io::ignore_io_error();
let path = Path::new(&"foo.txt");

let mut reader = BufferedReader::new(File::open(&path));
for line in reader.lines() {
Expand Down Expand Up @@ -415,12 +417,13 @@ To trap a condition, use `Condition::trap` in some caller of the site that calls
For example, this version of the program traps the `malformed_line` condition
and replaces bad input lines with the pair `(-1,-1)`:

~~~~{.xfail-test}
~~~~
# #[allow(unused_imports)];
# extern mod extra;
use std::io::buffered::BufferedReader;
use std::io::fs::File;
use std::io::File;
# mod BufferedReader {
# use std::io::fs::File;
# use std::io::File;
# use std::io::mem::MemReader;
# use std::io::buffered::BufferedReader;
# static s : &'static [u8] = bytes!("1 2\n\
Expand Down Expand Up @@ -452,8 +455,8 @@ fn main() {

fn read_int_pairs() -> ~[(int,int)] {
let mut pairs = ~[];
let args = std::os::args();
let path = Path::new(args.get_opt(1).expect("No input file parameter!").as_slice());
# let _g = std::io::ignore_io_error();
let path = Path::new(&"foo.txt");

let mut reader = BufferedReader::new(File::open(&path));
for line in reader.lines() {
Expand Down Expand Up @@ -490,12 +493,13 @@ In the example program, the first form of the `malformed_line` API implicitly as
This assumption may not be correct; some callers may wish to skip malformed lines, for example.
Changing the condition's return type from `(int,int)` to `Option<(int,int)>` will suffice to support this type of recovery:

~~~~{.xfail-test}
~~~~
# #[allow(unused_imports)];
# extern mod extra;
use std::io::buffered::BufferedReader;
use std::io::fs::File;
use std::io::File;
# mod BufferedReader {
# use std::io::fs::File;
# use std::io::File;
# use std::io::mem::MemReader;
# use std::io::buffered::BufferedReader;
# static s : &'static [u8] = bytes!("1 2\n\
Expand Down Expand Up @@ -528,8 +532,8 @@ fn main() {

fn read_int_pairs() -> ~[(int,int)] {
let mut pairs = ~[];
let args = std::os::args();
let path = Path::new(args.get_opt(1).expect("No input file parameter!").as_slice());
# let _g = std::io::ignore_io_error();
let path = Path::new(&"foo.txt");

let mut reader = BufferedReader::new(File::open(&path));
for line in reader.lines() {
Expand Down Expand Up @@ -575,12 +579,13 @@ until all relevant combinations encountered in practice are encoded.
In the example, suppose a third possible recovery form arose: reusing the previous value read.
This can be encoded in the handler API by introducing a helper type: `enum MalformedLineFix`.

~~~~{.xfail-test}
~~~~
# #[allow(unused_imports)];
# extern mod extra;
use std::io::buffered::BufferedReader;
use std::io::fs::File;
use std::io::File;
# mod BufferedReader {
# use std::io::fs::File;
# use std::io::File;
# use std::io::mem::MemReader;
# use std::io::buffered::BufferedReader;
# static s : &'static [u8] = bytes!("1 2\n\
Expand Down Expand Up @@ -622,8 +627,8 @@ fn main() {

fn read_int_pairs() -> ~[(int,int)] {
let mut pairs = ~[];
let args = std::os::args();
let path = Path::new(args.get_opt(1).expect("No input file parameter!").as_slice());
# let _g = std::io::ignore_io_error();
let path = Path::new(&"foo.txt");

let mut reader = BufferedReader::new(File::open(&path));
for line in reader.lines() {
Expand Down Expand Up @@ -699,12 +704,13 @@ task <unnamed> failed at 'called `Option::unwrap()` on a `None` value', .../libs
To make the program robust &mdash; or at least flexible &mdash; in the face of this potential failure,
a second condition and a helper function will suffice:

~~~~{.xfail-test}
~~~~
# #[allow(unused_imports)];
# extern mod extra;
use std::io::buffered::BufferedReader;
use std::io::fs::File;
use std::io::File;
# mod BufferedReader {
# use std::io::fs::File;
# use std::io::File;
# use std::io::mem::MemReader;
# use std::io::buffered::BufferedReader;
# static s : &'static [u8] = bytes!("1 2\n\
Expand Down Expand Up @@ -760,8 +766,8 @@ fn parse_int(x: &str) -> int {

fn read_int_pairs() -> ~[(int,int)] {
let mut pairs = ~[];
let args = std::os::args();
let path = Path::new(args.get_opt(1).expect("No input file parameter!").as_slice());
# let _g = std::io::ignore_io_error();
let path = Path::new(&"foo.txt");

let mut reader = BufferedReader::new(File::open(&path));
for line in reader.lines() {
Expand Down
66 changes: 45 additions & 21 deletions src/libstd/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,29 @@ while still providing feedback about errors. The basic strategy:
so that nullable values do not have to be 'unwrapped' before use.

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");
}
`File::create(&Path::new("diary.txt")).write(bytes!("Met a girl.\n"))`
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:

```rust
use std::io::File;
use std::io::{IoError, io_error};

let mut error = None;
io_error::cond.trap(|e: IoError| {
error = Some(e);
}).inside(|| {
File::create(&Path::new("diary.txt")).write(bytes!("Met a girl.\n"));
});

if error.is_some() {
println("failed to write my diary");
}
# ::std::io::fs::unlink(&Path::new("diary.txt"));
```

XXX: Need better condition handling syntax

Expand Down Expand Up @@ -498,10 +504,16 @@ pub trait Reader {
///
/// # Example
///
/// let mut reader = BufferedReader::new(File::open(&Path::new("foo.txt")));
/// for line in reader.lines() {
/// println(line);
/// }
/// ```rust
/// use std::io;
/// # let _g = ::std::io::ignore_io_error();
/// let mut reader = io::stdin();
///
/// let mut bytes = [0, .. 10];
/// reader.read(bytes);
///
/// if reader.eof() { println("stdin() had at most 10 bytes of data."); }
/// ```
///
/// # Failure
///
Expand Down Expand Up @@ -1057,6 +1069,18 @@ pub trait Buffer: Reader {
/// encoded unicode codepoints. If a newline is encountered, then the
/// newline is contained in the returned string.
///
/// # Example
///
/// ```rust
/// use std::io::buffered::BufferedReader;
/// use std::io;
/// # let _g = ::std::io::ignore_io_error();
///
/// let mut reader = BufferedReader::new(io::stdin());
///
/// let input = reader.read_line().unwrap_or(~"nothing");
/// ```
///
/// # Failure
///
/// This function will raise on the `io_error` condition (except for
Expand Down