/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 | } |