Expand description
Traits, helpers, and type definitions for core I/O functionality.
The std::io
module contains a number of common things you’ll need
when doing input and output. The most core part of this module is
the Read
and Write
traits, which provide the
most general interface for reading and writing input and output.
§Read and Write
Because they are traits, Read
and Write
are implemented by a number
of other types, and you can implement them for your types too. As such,
you’ll see a few different types of I/O throughout the documentation in
this module: File
s, TcpStream
s, and sometimes even Vec<T>
s. For
example, Read
adds a read
method, which we can use on
File
s:
use std::io;
use std::io::prelude::*;
use std::fs::File;
fn main() -> io::Result<()> {
let mut f = File::open("foo.txt")?;
let mut buffer = [0; 10];
// read up to 10 bytes
let n = f.read(&mut buffer)?;
println!("The bytes: {:?}", &buffer[..n]);
Ok(())
}
Read
and Write
are so important, implementors of the two traits have a
nickname: readers and writers. So you’ll sometimes see ‘a reader’ instead
of ‘a type that implements the Read
trait’. Much easier!
§Seek and BufRead
Beyond that, there are two important traits that are provided: Seek
and BufRead
. Both of these build on top of a reader to control
how the reading happens. Seek
lets you control where the next byte is
coming from:
use std::io;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::fs::File;
fn main() -> io::Result<()> {
let mut f = File::open("foo.txt")?;
let mut buffer = [0; 10];
// skip to the last 10 bytes of the file
f.seek(SeekFrom::End(-10))?;
// read up to 10 bytes
let n = f.read(&mut buffer)?;
println!("The bytes: {:?}", &buffer[..n]);
Ok(())
}
BufRead
uses an internal buffer to provide a number of other ways to read, but
to show it off, we’ll need to talk about buffers in general. Keep reading!
§BufReader and BufWriter
Byte-based interfaces are unwieldy and can be inefficient, as we’d need to be
making near-constant calls to the operating system. To help with this,
std::io
comes with two structs, BufReader
and BufWriter
, which wrap
readers and writers. The wrapper uses a buffer, reducing the number of
calls and providing nicer methods for accessing exactly what you want.
For example, BufReader
works with the BufRead
trait to add extra
methods to any reader:
use std::io;
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
fn main() -> io::Result<()> {
let f = File::open("foo.txt")?;
let mut reader = BufReader::new(f);
let mut buffer = String::new();
// read a line into buffer
reader.read_line(&mut buffer)?;
println!("{buffer}");
Ok(())
}
BufWriter
doesn’t add any new ways of writing; it just buffers every call
to write
:
use std::io;
use std::io::prelude::*;
use std::io::BufWriter;
use std::fs::File;
fn main() -> io::Result<()> {
let f = File::create("foo.txt")?;
{
let mut writer = BufWriter::new(f);
// write a byte to the buffer
writer.write(&[42])?;
} // the buffer is flushed once writer goes out of scope
Ok(())
}
§Standard input and output
A very common source of input is standard input:
use std::io;
fn main() -> io::Result<()> {
let mut input = String::new();
io::stdin().read_line(&mut input)?;
println!("You typed: {}", input.trim());
Ok(())
}
Note that you cannot use the ?
operator in functions that do not return
a Result<T, E>
. Instead, you can call .unwrap()
or match
on the return value to catch any possible errors:
And a very common source of output is standard output:
use std::io;
use std::io::prelude::*;
fn main() -> io::Result<()> {
io::stdout().write(&[42])?;
Ok(())
}
Of course, using io::stdout
directly is less common than something like
println!
.
§Iterator types
A large number of the structures provided by std::io
are for various
ways of iterating over I/O. For example, Lines
is used to split over
lines:
use std::io;
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
fn main() -> io::Result<()> {
let f = File::open("foo.txt")?;
let reader = BufReader::new(f);
for line in reader.lines() {
println!("{}", line?);
}
Ok(())
}
§Functions
There are a number of functions that offer access to various features. For example, we can use three of these functions to copy everything from standard input to standard output:
§io::Result
Last, but certainly not least, is io::Result
. This type is used
as the return type of many std::io
functions that can cause an error, and
can be returned from your own functions as well. Many of the examples in this
module use the ?
operator:
use std::io;
fn read_input() -> io::Result<()> {
let mut input = String::new();
io::stdin().read_line(&mut input)?;
println!("You typed: {}", input.trim());
Ok(())
}
The return type of read_input()
, io::Result<()>
, is a very
common type for functions which don’t have a ‘real’ return value, but do want to
return errors if they happen. In this case, the only purpose of this function is
to read the line and print it, so we use ()
.
§Platform-specific behavior
Many I/O functions throughout the standard library are documented to indicate what various library or syscalls they are delegated to. This is done to help applications both understand what’s happening under the hood as well as investigate any possibly unclear semantics. Note, however, that this is informative, not a binding contract. The implementation of many of these functions are subject to change over time and may call fewer or more syscalls/library functions.
§I/O Safety
Rust follows an I/O safety discipline that is comparable to its memory safety discipline. This
means that file descriptors can be exclusively owned. (Here, “file descriptor” is meant to
subsume similar concepts that exist across a wide range of operating systems even if they might
use a different name, such as “handle”.) An exclusively owned file descriptor is one that no
other code is allowed to access in any way, but the owner is allowed to access and even close
it any time. A type that owns its file descriptor should usually close it in its drop
function. Types like File
own their file descriptor. Similarly, file descriptors
can be borrowed, granting the temporary right to perform operations on this file descriptor.
This indicates that the file descriptor will not be closed for the lifetime of the borrow, but
it does not imply any right to close this file descriptor, since it will likely be owned by
someone else.
The platform-specific parts of the Rust standard library expose types that reflect these
concepts, see os::unix
and os::windows
.
To uphold I/O safety, it is crucial that no code acts on file descriptors it does not own or borrow, and no code closes file descriptors it does not own. In other words, a safe function that takes a regular integer, treats it as a file descriptor, and acts on it, is unsound.
Not upholding I/O safety and acting on a file descriptor without proof of ownership can lead to misbehavior and even Undefined Behavior in code that relies on ownership of its file descriptors: a closed file descriptor could be re-allocated, so the original owner of that file descriptor is now working on the wrong file. Some code might even rely on fully encapsulating its file descriptors with no operations being performed by any other part of the program.
Note that exclusive ownership of a file descriptor does not imply exclusive ownership of the
underlying kernel object that the file descriptor references (also called “open file description” on
some operating systems). File descriptors basically work like Arc
: when you receive an owned
file descriptor, you cannot know whether there are any other file descriptors that reference the
same kernel object. However, when you create a new kernel object, you know that you are holding
the only reference to it. Just be careful not to lend it to anyone, since they can obtain a
clone and then you can no longer know what the reference count is! In that sense, OwnedFd
is
like Arc
and BorrowedFd<'a>
is like &'a Arc
(and similar for the Windows types). In
particular, given a BorrowedFd<'a>
, you are not allowed to close the file descriptor – just
like how, given a &'a Arc
, you are not allowed to decrement the reference count and
potentially free the underlying object. There is no equivalent to Box
for file descriptors in
the standard library (that would be a type that guarantees that the reference count is 1
),
however, it would be possible for a crate to define a type with those semantics.
Re-exports§
pub use self::buffered::WriterPanicked;
pub use self::pipe::PipeReader;
pub use self::pipe::PipeWriter;
pub use self::pipe::pipe;
pub use self::stdio::IsTerminal;
pub use self::buffered::BufReader;
pub use self::buffered::BufWriter;
pub use self::buffered::IntoInnerError;
pub use self::buffered::LineWriter;
pub use self::copy::copy;
pub use self::cursor::Cursor;
pub use self::error::Error;
pub use self::error::ErrorKind;
pub use self::error::Result;
pub use self::stdio::Stderr;
pub use self::stdio::StderrLock;
pub use self::stdio::Stdin;
pub use self::stdio::StdinLock;
pub use self::stdio::Stdout;
pub use self::stdio::StdoutLock;
pub use self::stdio::stderr;
pub use self::stdio::stdin;
pub use self::stdio::stdout;
pub use self::util::Empty;
pub use self::util::Repeat;
pub use self::util::Sink;
pub use self::util::empty;
pub use self::util::repeat;
pub use self::util::sink;
pub use self::error::RawOsError;
Experimental #[doc(hidden)] pub use self::error::SimpleMessage;
Experimental pub use self::error::const_error;
Experimental #[doc(hidden)] pub use self::stdio::_eprint;
Experimental #[doc(hidden)] pub use self::stdio::_print;
Experimental #[doc(hidden)] pub use self::stdio::set_output_capture;
Experimental #[doc(hidden)] pub use self::stdio::try_set_output_capture;
Experimental
Modules§
- buffered 🔒
- Buffering wrappers for I/O traits
- copy 🔒
- cursor 🔒
- error 🔒
- impls 🔒
- pipe 🔒
- prelude
- The I/O Prelude.
- stdio 🔒
- util 🔒
Structs§
- Bytes
- An iterator over
u8
values of a reader. - Chain
- Adapter to chain together two readers.
- Guard 🔒
- IoSlice
- A buffer type used with
Write::write_vectored
. - IoSlice
Mut - A buffer type used with
Read::read_vectored
. - Lines
- An iterator over the lines of an instance of
BufRead
. - Split
- An iterator over the contents of an instance of
BufRead
split on a particular byte. - Take
- Reader adapter which limits the bytes read from an underlying reader.
- Borrowed
Buf Experimental - A borrowed byte buffer which is incrementally filled and initialized.
- Borrowed
Cursor Experimental - A writeable view of the unfilled portion of a
BorrowedBuf
.
Enums§
- Seek
From - Enumeration of possible methods to seek within an I/O object.
Constants§
Traits§
- BufRead
- A
BufRead
is a type ofRead
er which has an internal buffer, allowing it to perform extra ways of reading. - Read
- The
Read
trait allows for reading bytes from a source. - Seek
- The
Seek
trait provides a cursor which can be moved within a stream of bytes. - Size
Hint 🔒 - Spec
Read 🔒Byte - For the specialization of
Bytes::next
. - Write
- A trait for objects which are byte-oriented sinks.
Functions§
- append_
to_ 🔒 ⚠string - default_
read_ 🔒buf - default_
read_ 🔒buf_ exact - default_
read_ 🔒exact - default_
read_ 🔒to_ end - default_
read_ 🔒to_ string - default_
read_ 🔒vectored - default_
write_ 🔒fmt - default_
write_ 🔒vectored - inlined_
slow_ 🔒read_ byte - Reads a single byte in a slow, generic way. This is used by the default
spec_read_byte
. - read_
to_ string - Reads all bytes from a reader into a new
String
. - read_
until 🔒 - skip_
until 🔒 - uninlined_
slow_ 🔒read_ byte