Skip to content

Add read, read_string, and write functions to std::fs #45837

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

Merged
merged 6 commits into from
Dec 9, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
79 changes: 79 additions & 0 deletions src/libstd/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,73 @@ impl File {
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}

/// Read the entire contents of a file into a bytes vector.
///
/// This is a convenience function for using [`File::open`] and [`read_to_end`]
/// with fewer imports and without an intermediate variable.
///
/// [`File::open`]: struct.File.html#method.open
/// [`read_to_end`]: ../io/trait.Read.html#method.read_to_end
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open
///
/// It will also return an error if it encounters while reading an error
/// of a kind other than [`ErrorKind::Interrupted`].
///
/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted
///
/// # Examples
///
/// ```no_run
/// #![feature(file_read_write_contents)]
///
/// use std::fs::File;
///
/// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
/// let foo = String::from_utf8(File::read_contents("foo.txt")?)?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")]
pub fn read_contents<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let mut bytes = Vec::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could look up the file size first and use Vec::with_capacity. It might mean as extra system call when reading a tiny file, but it is a huge speedup (up to 2× on my system) when reading large files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe read_to_end should be responsible for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Through specialization on Seek.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specialization of read_to_end is a good idea. Filed #45851.

Note that callers of read_to_end can already avoid quadratic behavior by passing in a pre-allocated buffer. That workaround isn't available with the functions in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve opened a PR with a prototype: #45928

File::open(path)?.read_to_end(&mut bytes)?;
Ok(bytes)
}

/// Write the give contents to a file.
///
/// This function will create a file if it does not exist,
/// and will entirely replace its contents if it does.
///
/// This is a convenience function for using [`File::create`] and [`write_all`]
/// with fewer imports.
///
/// [`File::create`]: struct.File.html#method.create
/// [`write_all`]: ../io/trait.Write.html#method.write_all
///
/// # Examples
///
/// ```no_run
/// #![feature(file_read_write_contents)]
///
/// use std::fs::File;
///
/// # fn foo() -> std::io::Result<()> {
/// File::write_contents("foo.txt", b"Lorem ipsum")?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")]
pub fn write_contents<P: AsRef<Path>>(path: P, contents: &[u8]) -> io::Result<()> {
File::create(path)?.write_all(contents)
}

/// Attempts to sync all OS-internal metadata to disk.
///
/// This function will attempt to ensure that all in-core data reaches the
Expand Down Expand Up @@ -2921,6 +2988,18 @@ mod tests {
assert!(v == &bytes[..]);
}

#[test]
fn write_contents_then_read_contents() {
let mut bytes = [0; 1024];
StdRng::new().unwrap().fill_bytes(&mut bytes);

let tmpdir = tmpdir();

check!(File::write_contents(&tmpdir.join("test"), &bytes));
let v = check!(File::read_contents(&tmpdir.join("test")));
assert!(v == &bytes[..]);
}

#[test]
fn file_try_clone() {
let tmpdir = tmpdir();
Expand Down
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(exact_size_is_empty)]
#![feature(file_read_write_contents)]
#![feature(fixed_size_array)]
#![feature(float_from_str_radix)]
#![feature(fn_traits)]
Expand Down