Expand description
A utility library for reading data from input and writing data to output.
§Principles
- 
Simple: You can read and write data with a single line of code. 
- 
Flexible: You can customize the format of data if the default format does not meet your needs. 
- 
Efficient: You can read and write data with minimal overhead. 
- 
Safe: You can read and write data without worrying about buffer overflow or other security issues. 
- 
Easy to Learn: You can read and write data with a similar interface to Python3 and C++. 
- 
Extensible: You can implement your own types to read and write data. 
- 
Compatible: You can read and write data with types that implement std::fmt::Display and std::str::FromStr. 
- 
Human Readable: You can read and write data in a human-readable format. For types whose representation does not have a fixed length in characters, the default separator is a space; otherwise, such as for char, it is an empty string. 
§In Short
§read!
You can use read! macro to read a single data item, a Vec or a Mat from input.
- read!()reads a single data item from input.
- read!(n)reads- ndata items from input and stores them in a Vec.
- read!(m, n)reads- m * ndata items from input and stores them in a Mat.
Given the input below:
42 abc def
0 0.3 lmn
1 2 3
1 2 3
4 5 6
.@/#$
!@#!@
*&@:,use iof::{read, Mat};
/// Some examples of reading from standard input.
///
/// We use a `v` to indicate the cursor position.
fn main() {
    // Read a single integer from input.
    //
    // v
    // 42 abc def
    let n: u32 = read!();
    assert_eq!(n, 42);
    // Read a single string from input.
    //
    //    v
    // 42 abc def
    let n: String = read!();
    assert_eq!(n, "abc");
    // Read a vector of characters from input.
    // Spaces are ignored, and those characters need not be separated by spaces.
    //
    //        v
    // 42 abc def
    let v: Vec<char> = read!();
    assert_eq!(v, ['d', 'e', 'f']);
    // Read a tuple from input. Equivalent to:
    //
    // ```
    // let l: u32 = read!();
    // let m: f64 = read!();
    // let n: String = read!();
    // ```
    //
    // v
    // 0 0.3 lmn
    let (l, m, n): (u32, f64, String) = read!();
    assert_eq!(l, 0);
    assert_eq!(m, 0.3);
    assert_eq!(n, "lmn");
    // Read a vector of integers from input.
    // They are separated by spaces.
    //
    // v
    // 1 2 3
    let v: Vec<u32> = read!(3);
    assert_eq!(v, [1, 2, 3]);
    // Read a matrix of integers from input.
    // They are separated by spaces, and newlines are unnecessary but useful for readability.
    //
    // v
    // 1 2 3
    // 4 5 6
    let m: Mat<u32> = read!(2, 3);
    assert_eq!(m, [[1, 2, 3], [4, 5, 6]]);
    // Read a matrix of characters from input.
    // Spaces are ignored and unnecessary, too.
    //
    // v
    // .@/#$
    // !@#!@
    // *&@:,
    let m: Mat<char> = read!(3, 5);
    assert_eq!(
        m,
        [
            ['.', '@', '/', '#', '$'],
            ['!', '@', '#', '!', '@'],
            ['*', '&', '@', ':', ',']
        ]
    );
}§get_line and get_line_some
You can use get_line functions to read a line of string from current position of cursor in standard input to the end of the line.
Given the input below:
42
abcuse iof::{get_line, read};
fn main() {
    // Read a single string from input.
    //
    // v
    // 42
    // abc
    let s: String = read!();
    assert_eq!(s, "42");
    // Read a line of string from input.
    // Before reading, the cursor is at the end of the previous line.
    // Therefore, it reads an empty string.
    //
    //   v
    // 42
    // abc
    let s: String = get_line();
    assert_eq!(s, "");
    // Read a line of string from input.
    //
    // v
    // abc
    let s: String = get_line();
    assert_eq!(s, "abc");
}You may have noticed that the get_line function is similar to the input function in Python3 and std::get_line in C++.
Sometimes you may want to ensure that the line is not empty. You can use get_line_some functions to read a non-empty line of string from the position of next non-whitespace character to the end of the line.
Given the input below:
42
abcuse iof::{get_line_some, read};
fn main() {
    // Read a single string from input.
    //
    // v
    // 42
    // abc
    let s: String = read!();
    assert_eq!(s, "42");
    // Read a non-empty line of string from input.
    // Before reading, the cursor is at the end of the previous line.
    // However, as the function name implies, it will repeatedly read lines until a non-empty line is found.
    //
    //   v
    // 42
    // abc
    let s: String = get_line_some();
    assert_eq!(s, "abc");
}See Cursor for more details.
§show!
You can use show! macro to write something to output.
- show!(e)writes- eto output. They are formatted with default format.
- show!([a, b], sep = [", "])writes- [a, b]like a- Vecto output. They are separated by a comma and a space, as specified in the- sepparameter.
- show!([[a, b], [c, d]], sep = ["\n", " "])writes- elike a- Matto output. Rows of them are separated by LF, and columns are separated by a space, as specified in the- sepparameter.
Also, you can append a => and a buffer to write to a buffer instead of standard output, such as show!(e => buf) and show!([a, b], sep = [", "] => buf).
Note that all parameters are optional and placed after a comma, and the order of parameters does not matter. The default value of sep and the default value of end are from the get_default_separator function. See Separator for more details.
You may have noticed that the show! macro is similar to the print function in Python.
Code below writes the output to standard output:
use iof::show;
fn main() {
    // Write a single integer to output.
    show!(42);
    // Write a single string to output.
    show!("Hello, World!");
    // Write a vector of integers to output. There will be a space between the integers.
    show!([1, 2, 3]);
    // Write a matrix of integers to output. There will be a newline between the rows, and a space between the integers.
    show!([[1, 2, 3], [4, 5, 6]]);
    // Write a matrix of characters to output. There will be a newline between the rows, and no space between the characters.
    show!([['.', '@', '/'], ['#', '$', '$']]);
    // Write a tuple to output.
    show!((1, 2, 3));
    // Write a tuple of tuples to output.
    show!(((1, 2), (3, 4)));
    // Write an empty tuple to output.
    show!(());
    // Write a tuple of vectors to output.
    show!(([1, 2], [3, 4]));
    // Write a 3-dimensional vector to output with custom separators.
    show!(
        [[[1, 2], [3, 4]], [[5, 6], [7, 8]]],
        sep = [" | ", " :: ", " "],
    );
    // Write several strings to output and append an exclamation mark.
    show!(("Hello", "World"), sep = ", ", end = "!\n");
}And code above generates the output below:
42
Hello, World!
1 2 3
1 2 3
4 5 6
.@/
#$$
1 2 3
1 2
3 4
1 2
3 4
1 2 :: 3 4 | 5 6 :: 7 8
Hello, World!§Input
§ReadInto
Some higher-level functions are provided to read data sequence (a single item is also a sequence) from input:
- read<T>()(or- try_read<T>()) reads a single sequence from input and converts it to a value of- T.
- read_n<T>(n)(or- try_read_n<T>(n)) reads- nsequences from input and converts them to a value of Vec.
- read_m_n<T>(m, n)(or- try_read_m_n<T>(m, n)) reads- m * nsequences from input and converts them to a value of- Mat<T>.
These functions are implemented for types that implement ReadInto trait. Currently, the following types implement ReadInto trait:
- All types that implement ReadOneFrom trait;
- [T; N]where- Timplements ReadInto trait;
- Box<[T; N]>where- Timplements ReadInto trait.
- Tuple types, e.g., (T1, T2, ..., Tn), whereTiimplements ReadInto trait andnis neither 0 nor more than 12.
- …
§ReadOneFrom
Some lower-level functions are provided to read a single data item from input:
- 
read_one<T>()(ortry_read_one<T>()) reads a ASCII-whitespace-separated fragment of string (for char, it reads a single non-ASCII-whitespace character instead), and converts it to a value ofT.The fragment is a string that does not contain any ASCII whitespace characters, and ASCII whitespace characters here are defined as [' ', '\t', '\n', '\r'].For example, given the input below: 1 2,3 3 4;saIf you call read_one<String>()for 4 times, it will read 4 string fragments1,2,3,3, and4;sa.
- 
read_in_line_some_trimmed<T>()(ortry_read_in_line_some_trimmed<T>()) reads a non-empty line of string from input, trims the trailing newline, and converts it to a value ofT.If all characters in remained part of current line are ASCII whitespace characters, it will discard current line and read one more line, otherwise it will return remained part of current line. For example, given the input below: 1 2 3 4 5 6If you call read_in_line_some_trimmed<T>()for 2 times, it will read1 2 3and4 5 6as two lines of string.And given the input below: 1 2 3If you call read_one<T>()once andread_in_line_some_trimmed<T>()once, they will read1and2 3respectively.If you call read_in_line_some_trimmed<T>()once more, it will panic because there is no non-empty line remained.Again, given the input below: 1 2 3 4 5 6If you call read_one<T>()for 3 times andread_in_line_some_trimmed<T>()for 1 time, they will read1,2,3, and4 5 6respectively.
- 
read_in_line_trimmed<T>()(orread_in_line_trimmed<T>()) reads the remained line from input regardless of whether it is empty or not, trims the trailing newline, and converts it to a value ofT.For example, given the input below: 1 2 3 4 5 6If you call read_one<T>for 3 times andread_in_line_trimmed<T>for 1 time, they will read1,2,3, and an empty string respectively.If you call read_in_line_trimmed<T>once more, it will still read an empty string.
- 
read_in_char<T>(ortry_read_in_char<T>) reads a single non-ASCII-whitespace character from input and converts it to a value ofT.For example, given the input below: 1 2 3If you call read_in_charfor 3 times, it will read1,2, and3as three characters.
- 
read_all<T>()(ortry_read_all<T>()) reads all remaining data items from input and converts them to a value of Vec.
- 
read_any_in_line<T>()(ortry_read_any_in_line<T>()) reads all data items in current line from input and converts them to a value of Vec.
- 
read_some_in_line<T>()(ortry_read_some_in_line<T>()) reads all data items in the next non-empty line from input and converts them to a value of Vec.
These functions are implemented for types that implement ReadOneFrom trait. Currently, the following types in std (or core) implement ReadOneFrom trait:
- String and ASCIIString;
- char and ASCIIChar (but it has different behavior from other types);
- u8, u16, u32, u64, u128, usize;
- i8, i16, i32, i64, i128, isize;
- f32, f64;
- bool;
- NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize;
- NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize;
- …
And you can implement ReadOneFrom trait for your own types by implementing ReadOneFrom::parse method. For FromStr types, you can use the macro impl_read_one_from_for_from_str!.
§Extra ASCII Support
For ASCII characters, you can use ASCIIChar and ASCIIString to read and write them.
Given the input below:
1
23
456
789Code below reads the input and stores it in variables:
use iof::{read_m_n, read_n, read_one, ASCIIChar, Mat};
fn main() {
    let c: ASCIIChar = read_one();
    assert_eq!(c, ASCIIChar::Digit1);
    let v: Vec<ASCIIChar> = read_n(2);
    assert_eq!(v, [ASCIIChar::Digit2, ASCIIChar::Digit3]);
    let m: Mat<ASCIIChar> = read_m_n(2, 3);
    assert_eq!(
        m,
        [
            [ASCIIChar::Digit4, ASCIIChar::Digit5, ASCIIChar::Digit6],
            [ASCIIChar::Digit7, ASCIIChar::Digit8, ASCIIChar::Digit9],
        ]
    );
}§Complex Examples for Input
Function read() is the simplest way to read data from standard input.
Given the input below:
42
Hello!
1 2 3 4
1 2
3 4Code below reads the input and stores it in variables:
use iof::read;
/// Some examples of reading from standard input using the `read` function.
fn main() {
    // Read a single integer from input.
    let n: u32 = read();
    assert_eq!(n, 42);
    // Read a string from input.
    let s: String = read();
    assert_eq!(s, "Hello!");
    // Read an array of integers from input.
    let arr: [u32; 4] = read();
    assert_eq!(arr, [1, 2, 3, 4]);
    // Read a nested array of integers from input.
    let arr: [[u32; 2]; 2] = read();
    assert_eq!(arr, [[1, 2], [3, 4]]);
}§Output
§SepBy and sep_by!
Some lower-level functions are provided to write a data sequence with customizing format to output:
- SepBy::sep_by(iter, sep) writes a sequence of data items from iter, which implements IntoIterator, to output with a separatorsep.
- sep_by!(iter, sep) writes a sequence of data items from iter, which implements IntoIterator, to output with a separatorsep.
There won’t be any separator before the first item or after the last item.
For example:
use iof::{sep_by, SepBy};
use std::collections::{BTreeMap, BTreeSet};
let v = vec![1, 2, 3];
let s = sep_by!(&v, ", ");
assert_eq!(s.to_string(), "1, 2, 3");
// Equivalent form:
let s = v.sep_by(", ");
assert_eq!(s.to_string(), "1, 2, 3");
let v = vec![vec![1, 2, 3], vec![4, 5, 6]];
let s = sep_by!(&v, "\n", ", ");
assert_eq!(s.to_string(), "1, 2, 3\n4, 5, 6");
// Equivalent form:
let s = v.iter().map(|e| e.sep_by(", ")).sep_by("\n");
assert_eq!(s.to_string(), "1, 2, 3\n4, 5, 6");
let v = BTreeSet::from_iter([3, 1, 2, 4]);
let s = sep_by!(&v, ", ");
assert_eq!(s.to_string(), "1, 2, 3, 4");
// Equivalent form:
let s = v.sep_by(", ");
assert_eq!(s.to_string(), "1, 2, 3, 4");
let v = BTreeMap::from_iter([(3, "w"), (1, "x"), (2, "y"), (4, "z")]);
let s = sep_by!(v.iter().map(|(k, v)| format!("{} -> {}", k, v)), "\n");
assert_eq!(s.to_string(), "1 -> x\n2 -> y\n3 -> w\n4 -> z");
// Equivalent form:
let s = v.iter().map(|(k, v)| format!("{} -> {}", k, v)).sep_by("\n");
assert_eq!(s.to_string(), "1 -> x\n2 -> y\n3 -> w\n4 -> z");Note that the iterator must implement Clone trait to use the SepBy trait. And due to this constraint, if you write a container directly as the argument of sep_by!, you may need to use & to borrow it.
And created objects can also be used in some formats other than Display format or Debug format.
use iof::{sep_by, SepBy};
use std::f64::consts::*;
let v = vec![1.0, 2.1, 3.2];
let s = sep_by!(&v, ", ");
assert_eq!(format!("{s:?}"), "1.0, 2.1, 3.2");
let v = vec!["Alice", "Bob", "Charlie"];
let s = sep_by!(&v, ";");
assert_eq!(format!("{s:>10}"), "     Alice;       Bob;   Charlie");
assert_eq!(format!("{s:<10}"), "Alice     ;Bob       ;Charlie   ");
let v = vec![E, PI, FRAC_1_PI, LN_2, LN_10, SQRT_2];
let s = sep_by!(&v, "");
assert_eq!(format!("{s:15.7}"), "      2.7182818      3.1415927      0.3183099      0.6931472      2.3025851      1.4142136");
let v = vec![3735928559u32, 3405691582u32, 3405709037u32, 3435973836u32, 3452816845u32];
let s = sep_by!(&v, " ");
assert_eq!(format!("{s:x}"), "deadbeef cafebabe cafefeed cccccccc cdcdcdcd");§WriteInto
Some higher-level functions are provided to write data sequence with default format to output:
- WriteInto::try_write() writes to standard output with default format.
- WriteInto::try_write_into() writes to given buffer that implements std::io::Write with default format.
- WriteInto::try_write_into_string() writes to a new string with default format.
The default format is defined as follows:
- For types that implement Display trait (but we only implement WriteInto for a part of types that implement Display), it uses Display::fmt method;
- For String, it writes the string as is;
- For char, it writes the character as is;
- For u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, it writes the value as is in decimal format;
 
- For [T],[T; N]and Vec whereTimplements WriteInto trait, it writes each item in the vector with a space as separator;
- For Mat where Timplements WriteInto trait, it writes each row in the matrix with a newline as separator, and writes each item in a row with a space as separator;
- For all &TwhereTimplements WriteInto trait, it writes the value as is.
And you can implement WriteInto trait for your own types by implementing WriteInto::try_write_into_with_sep method. For Display types, you can use the macro impl_write_into_for_display!.
§Separator
The separator is a string that separates data items. It can be a single character, a string, or a slice of strings.
The default separator from get_default_separator is defined as follows:
- For all types whose dimension is 0, it uses [];
- For all types whose dimension is 1 and Tmust be separated by a space, it uses[" "];
- For all types whose dimension is 1 and Tneed not be separated by a space, it uses[""];
- For all types whose dimension is 2 and Tmust be separated by a space, it uses["\n", " "];
- For all types whose dimension is 2 and Tneed not be separated by a space, it uses["\n", ""].
- …
The dimension of a type is the number of dimensions of the data sequence. For example, the dimension of a primitive type T is 0, the dimension of Vec<T> is 1, and the dimension of Mat<T> is 2.
§Notes
§Concurrency
Take care when using this library in a multi-threaded environment, as the standard input/output streams are shared among all threads. See Stdin and Stdout for more details.
§Cursor
For character streams, The cursor is the position of the next character to be read. It is at the beginning of the input stream initially, and it moves forward as data items are read.
In general, every call to a read function that consume a data item will consume the input up to the next whitespace character (but white spaces after the data item will not be consumed), and every call to a read function that reads a line will consume the input up to the next newline character (and then the cursor will be at the beginning of the next line).
It’s sometimes a little tricky to determine the position of the cursor. For example, given the input below:
1 2 3
4 5 6If you call read_one<String>() for 3 times and read_in_line_trimmed<String>() for 1 time, they will read 1, 2, 3, and an empty string respectively. Therefore it’s generally unrecommended to use read_in_line_trimmed<String>() and similar functions that read a possibly empty line of string without specifying the number of data items to read.
Re-exports§
- pub use crate as iof;
Modules§
- ascii
- A port of the asciimodule from Rust’s standard library with a little bit of extra functionality.
- dimension
- Dimension markers.
- ext
- Extensions for characters and strings.
- fmt
- Format trait for input format and built-in formats.
- separator
- Separator and default separator.
- utf8char
- UTF-8 character types.
Macros§
- argument_or_ default 
- Return the given expression or the default value.
- impl_read_ one_ from_ for_ from_ str 
- Implement ReadOneFrom for given types that already implement std::str::FromStr.
- impl_write_ into_ for_ display 
- Implement WriteInto for given types that already implements std::fmt::Display.
- read
- Read a single data item, a Vec or a Mat from input using ReadInto.
- sep_by
- Create an object using given separator.
- show
- Write the given expression into standard output using WriteInto.
- unwrap
- Unwrap a result or panic with the error message.
Structs§
- ASCIIString 
- A string that only contains ASCII characters.
- DefaultSeparator 
- Use default separator.
- InputStream 
- C++-like Stream.
- Vec
- A contiguous growable array type, written as Vec<T>, short for ‘vector’.
Enums§
- ASCIIChar 
- An ASCII character. This is a subset of the Unicode character set.
- ReadError 
- Error during using ReadInto or ReadOneFrom.
Traits§
- BufReadExt 
- Extension trait for BufRead.
- BufReadExtWith Format 
- Extension trait for BufReadExt with CharSet and Pattern.
- ReadFrom 
- Read data from input stream.
- ReadInto 
- The opposite of ReadFrom.
- ReadOneFrom 
- Read a single data item from input stream.
- ReadOneInto 
- The opposite of ReadOneFrom.
- SepBy
- Create a new object using given iterator and separator.
- Separators
- Separator by.
- WriteInto 
- Write into a stream.
Functions§
- get_line 
- Read a line from standard input. The trailing newline will be consumed and trimmed.
- get_line_ some 
- Read a non-empty line from standard input. The trailing newline will be consumed and trimmed.
- read
- Unwrap the result of try_read.
- read_all 
- Unwrap the result of try_read_all.
- read_any_ in_ line 
- Unwrap the result of try_read_any_in_line.
- read_in_ char 
- Unwrap the result of try_read_in_char.
- read_in_ line_ some_ trimmed 
- Unwrap the result of try_read_in_line_some_trimmed.
- read_in_ line_ trimmed 
- Unwrap the result of try_read_in_line_trimmed.
- read_m_ n 
- Unwrap the result of try_read_m_n.
- read_mat 
- Unwrap the result of try_read_m_n.
- read_n
- Unwrap the result of try_read_n.
- read_one 
- Unwrap the result of try_read_one.
- read_some_ in_ line 
- Unwrap the result of try_read_some_in_line.
- read_vec 
- Unwrap the result of try_read_n.
- stdin
- Get an exclusive handle to the standard input stream.
- stdout
- Get an exclusive handle to the standard output stream.
- try_read 
- Call ReadInto::try_readon stdin.
- try_read_ all 
- Call ReadOneInto::try_read_allon stdin.
- try_read_ any_ in_ line 
- Call ReadOneInto::try_read_any_in_lineon stdin.
- try_read_ in_ char 
- Call ReadOneInto::try_read_in_charon stdin.
- try_read_ in_ line_ some_ trimmed 
- Call ReadOneInto::try_read_in_line_some_trimmedon stdin.
- try_read_ in_ line_ trimmed 
- Call ReadOneInto::try_read_in_line_trimmedon stdin.
- try_read_ m_ n 
- Call ReadInto::try_read_m_non stdin.
- try_read_ mat 
- Call ReadInto::try_read_m_non stdin.
- try_read_ n 
- Call ReadInto::try_read_non stdin.
- try_read_ one 
- Call ReadOneInto::try_read_oneon stdin.
- try_read_ some_ in_ line 
- Call ReadOneInto::try_read_some_in_lineon stdin.
- try_read_ vec 
- Call ReadInto::try_read_non stdin.
- write
- Write the given value into the buffer.
Type Aliases§
- Mat
- A matrix with mrows andncolumns.
- ReadFrom Error 
- The error type for ReadFrom.
- ReadOneFrom Error 
- The error type for ReadOneFrom.