Coverage Report

Created: 2025-08-26 07:09

/rust/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.47.1/src/fs/mod.rs
Line
Count
Source (jump to first uncovered line)
1
#![cfg(not(loom))]
2
3
//! Asynchronous file utilities.
4
//!
5
//! This module contains utility methods for working with the file system
6
//! asynchronously. This includes reading/writing to files, and working with
7
//! directories.
8
//!
9
//! Be aware that most operating systems do not provide asynchronous file system
10
//! APIs. Because of that, Tokio will use ordinary blocking file operations
11
//! behind the scenes. This is done using the [`spawn_blocking`] threadpool to
12
//! run them in the background.
13
//!
14
//! The `tokio::fs` module should only be used for ordinary files. Trying to use
15
//! it with e.g., a named pipe on Linux can result in surprising behavior,
16
//! such as hangs during runtime shutdown. For special files, you should use a
17
//! dedicated type such as [`tokio::net::unix::pipe`] or [`AsyncFd`] instead.
18
//!
19
//! Currently, Tokio will always use [`spawn_blocking`] on all platforms, but it
20
//! may be changed to use asynchronous file system APIs such as io_uring in the
21
//! future.
22
//!
23
//! # Usage
24
//!
25
//! The easiest way to use this module is to use the utility functions that
26
//! operate on entire files:
27
//!
28
//!  * [`tokio::fs::read`](fn@crate::fs::read)
29
//!  * [`tokio::fs::read_to_string`](fn@crate::fs::read_to_string)
30
//!  * [`tokio::fs::write`](fn@crate::fs::write)
31
//!
32
//! The two `read` functions reads the entire file and returns its contents.
33
//! The `write` function takes the contents of the file and writes those
34
//! contents to the file. It overwrites the existing file, if any.
35
//!
36
//! For example, to read the file:
37
//!
38
//! ```
39
//! # async fn dox() -> std::io::Result<()> {
40
//! let contents = tokio::fs::read_to_string("my_file.txt").await?;
41
//!
42
//! println!("File has {} lines.", contents.lines().count());
43
//! # Ok(())
44
//! # }
45
//! ```
46
//!
47
//! To overwrite the file:
48
//!
49
//! ```
50
//! # async fn dox() -> std::io::Result<()> {
51
//! let contents = "First line.\nSecond line.\nThird line.\n";
52
//!
53
//! tokio::fs::write("my_file.txt", contents.as_bytes()).await?;
54
//! # Ok(())
55
//! # }
56
//! ```
57
//!
58
//! ## Using `File`
59
//!
60
//! The main type for interacting with files is [`File`]. It can be used to read
61
//! from and write to a given file. This is done using the [`AsyncRead`] and
62
//! [`AsyncWrite`] traits. This type is generally used when you want to do
63
//! something more complex than just reading or writing the entire contents in
64
//! one go.
65
//!
66
//! **Note:** It is important to use [`flush`] when writing to a Tokio
67
//! [`File`]. This is because calls to `write` will return before the write has
68
//! finished, and [`flush`] will wait for the write to finish. (The write will
69
//! happen even if you don't flush; it will just happen later.) This is
70
//! different from [`std::fs::File`], and is due to the fact that `File` uses
71
//! `spawn_blocking` behind the scenes.
72
//!
73
//! For example, to count the number of lines in a file without loading the
74
//! entire file into memory:
75
//!
76
//! ```no_run
77
//! use tokio::fs::File;
78
//! use tokio::io::AsyncReadExt;
79
//!
80
//! # async fn dox() -> std::io::Result<()> {
81
//! let mut file = File::open("my_file.txt").await?;
82
//!
83
//! let mut chunk = vec![0; 4096];
84
//! let mut number_of_lines = 0;
85
//! loop {
86
//!     let len = file.read(&mut chunk).await?;
87
//!     if len == 0 {
88
//!         // Length of zero means end of file.
89
//!         break;
90
//!     }
91
//!     for &b in &chunk[..len] {
92
//!         if b == b'\n' {
93
//!             number_of_lines += 1;
94
//!         }
95
//!     }
96
//! }
97
//!
98
//! println!("File has {} lines.", number_of_lines);
99
//! # Ok(())
100
//! # }
101
//! ```
102
//!
103
//! For example, to write a file line-by-line:
104
//!
105
//! ```no_run
106
//! use tokio::fs::File;
107
//! use tokio::io::AsyncWriteExt;
108
//!
109
//! # async fn dox() -> std::io::Result<()> {
110
//! let mut file = File::create("my_file.txt").await?;
111
//!
112
//! file.write_all(b"First line.\n").await?;
113
//! file.write_all(b"Second line.\n").await?;
114
//! file.write_all(b"Third line.\n").await?;
115
//!
116
//! // Remember to call `flush` after writing!
117
//! file.flush().await?;
118
//! # Ok(())
119
//! # }
120
//! ```
121
//!
122
//! ## Tuning your file IO
123
//!
124
//! Tokio's file uses [`spawn_blocking`] behind the scenes, and this has serious
125
//! performance consequences. To get good performance with file IO on Tokio, it
126
//! is recommended to batch your operations into as few `spawn_blocking` calls
127
//! as possible.
128
//!
129
//! One example of this difference can be seen by comparing the two reading
130
//! examples above. The first example uses [`tokio::fs::read`], which reads the
131
//! entire file in a single `spawn_blocking` call, and then returns it. The
132
//! second example will read the file in chunks using many `spawn_blocking`
133
//! calls. This means that the second example will most likely be more expensive
134
//! for large files. (Of course, using chunks may be necessary for very large
135
//! files that don't fit in memory.)
136
//!
137
//! The following examples will show some strategies for this:
138
//!
139
//! When creating a file, write the data to a `String` or `Vec<u8>` and then
140
//! write the entire file in a single `spawn_blocking` call with
141
//! `tokio::fs::write`.
142
//!
143
//! ```no_run
144
//! # async fn dox() -> std::io::Result<()> {
145
//! let mut contents = String::new();
146
//!
147
//! contents.push_str("First line.\n");
148
//! contents.push_str("Second line.\n");
149
//! contents.push_str("Third line.\n");
150
//!
151
//! tokio::fs::write("my_file.txt", contents.as_bytes()).await?;
152
//! # Ok(())
153
//! # }
154
//! ```
155
//!
156
//! Use [`BufReader`] and [`BufWriter`] to buffer many small reads or writes
157
//! into a few large ones. This example will most likely only perform one
158
//! `spawn_blocking` call.
159
//!
160
//! ```no_run
161
//! use tokio::fs::File;
162
//! use tokio::io::{AsyncWriteExt, BufWriter};
163
//!
164
//! # async fn dox() -> std::io::Result<()> {
165
//! let mut file = BufWriter::new(File::create("my_file.txt").await?);
166
//!
167
//! file.write_all(b"First line.\n").await?;
168
//! file.write_all(b"Second line.\n").await?;
169
//! file.write_all(b"Third line.\n").await?;
170
//!
171
//! // Due to the BufWriter, the actual write and spawn_blocking
172
//! // call happens when you flush.
173
//! file.flush().await?;
174
//! # Ok(())
175
//! # }
176
//! ```
177
//!
178
//! Manually use [`std::fs`] inside [`spawn_blocking`].
179
//!
180
//! ```no_run
181
//! use std::fs::File;
182
//! use std::io::{self, Write};
183
//! use tokio::task::spawn_blocking;
184
//!
185
//! # async fn dox() -> std::io::Result<()> {
186
//! spawn_blocking(move || {
187
//!     let mut file = File::create("my_file.txt")?;
188
//!
189
//!     file.write_all(b"First line.\n")?;
190
//!     file.write_all(b"Second line.\n")?;
191
//!     file.write_all(b"Third line.\n")?;
192
//!
193
//!     // Unlike Tokio's file, the std::fs file does
194
//!     // not need flush.
195
//!
196
//!     io::Result::Ok(())
197
//! }).await.unwrap()?;
198
//! # Ok(())
199
//! # }
200
//! ```
201
//!
202
//! It's also good to be aware of [`File::set_max_buf_size`], which controls the
203
//! maximum amount of bytes that Tokio's [`File`] will read or write in a single
204
//! [`spawn_blocking`] call. The default is two megabytes, but this is subject
205
//! to change.
206
//!
207
//! [`spawn_blocking`]: fn@crate::task::spawn_blocking
208
//! [`AsyncRead`]: trait@crate::io::AsyncRead
209
//! [`AsyncWrite`]: trait@crate::io::AsyncWrite
210
//! [`BufReader`]: struct@crate::io::BufReader
211
//! [`BufWriter`]: struct@crate::io::BufWriter
212
//! [`tokio::net::unix::pipe`]: crate::net::unix::pipe
213
//! [`AsyncFd`]: crate::io::unix::AsyncFd
214
//! [`flush`]: crate::io::AsyncWriteExt::flush
215
//! [`tokio::fs::read`]: fn@crate::fs::read
216
217
mod canonicalize;
218
pub use self::canonicalize::canonicalize;
219
220
mod create_dir;
221
pub use self::create_dir::create_dir;
222
223
mod create_dir_all;
224
pub use self::create_dir_all::create_dir_all;
225
226
mod dir_builder;
227
pub use self::dir_builder::DirBuilder;
228
229
mod file;
230
pub use self::file::File;
231
232
mod hard_link;
233
pub use self::hard_link::hard_link;
234
235
mod metadata;
236
pub use self::metadata::metadata;
237
238
mod open_options;
239
pub use self::open_options::OpenOptions;
240
241
mod read;
242
pub use self::read::read;
243
244
mod read_dir;
245
pub use self::read_dir::{read_dir, DirEntry, ReadDir};
246
247
mod read_link;
248
pub use self::read_link::read_link;
249
250
mod read_to_string;
251
pub use self::read_to_string::read_to_string;
252
253
mod remove_dir;
254
pub use self::remove_dir::remove_dir;
255
256
mod remove_dir_all;
257
pub use self::remove_dir_all::remove_dir_all;
258
259
mod remove_file;
260
pub use self::remove_file::remove_file;
261
262
mod rename;
263
pub use self::rename::rename;
264
265
mod set_permissions;
266
pub use self::set_permissions::set_permissions;
267
268
mod symlink_metadata;
269
pub use self::symlink_metadata::symlink_metadata;
270
271
mod write;
272
pub use self::write::write;
273
274
mod copy;
275
pub use self::copy::copy;
276
277
mod try_exists;
278
pub use self::try_exists::try_exists;
279
280
#[cfg(test)]
281
mod mocks;
282
283
feature! {
284
    #![unix]
285
286
    mod symlink;
287
    pub use self::symlink::symlink;
288
}
289
290
cfg_windows! {
291
    mod symlink_dir;
292
    pub use self::symlink_dir::symlink_dir;
293
294
    mod symlink_file;
295
    pub use self::symlink_file::symlink_file;
296
}
297
298
use std::io;
299
300
#[cfg(not(test))]
301
use crate::blocking::spawn_blocking;
302
#[cfg(test)]
303
use mocks::spawn_blocking;
304
305
0
pub(crate) async fn asyncify<F, T>(f: F) -> io::Result<T>
306
0
where
307
0
    F: FnOnce() -> io::Result<T> + Send + 'static,
308
0
    T: Send + 'static,
309
0
{
310
0
    match spawn_blocking(f).await {
311
0
        Ok(res) => res,
312
0
        Err(_) => Err(io::Error::new(
313
0
            io::ErrorKind::Other,
314
0
            "background task failed",
315
0
        )),
316
    }
317
0
}