Skip to content
Select theme

How to read a file into bytes

Short answer: std::fs::read(path) reads a whole file and gives back an io::Result<Vec<u8>> — every byte on disk, with no assumption about what they mean. Reach for it whenever the file isn’t guaranteed to be UTF-8 text: the same file that fails read_to_string with InvalidData reads fine here.

From a fresh project — cargo new read-demo, then replace the contents of src/main.rs with each block below and run it with cargo run. The project name shows up only in cargo’s own output; nothing else on this page depends on it. Each block writes the file it reads before reading it, so it runs on its own; in your own code the file is already there and you delete that line.

89 50 4E 47 0D 0A 1A 0A E9
9 bytes

One call opens the file, reads it to the end and closes it. What comes back is a Vec<u8> — one element per byte, so len() is the size on disk and indexing gives you a single byte. {:?} on a Vec<u8> prints decimals ([137, 80, 78, 71, …]), and file formats are written in hex: 89 50 4E 47 0D 0A 1A 0A is the PNG signature, the first eight bytes of the file.

fs::read is File::open plus read_to_end, with the file’s size taken from its metadata as a capacity hint. The long form is what you want when there’s already a buffer to fill — reusing one Vec across files avoids reallocating each time:

9 bytes

read_to_end grows the buffer as bytes arrive, which is also the cost to know about: both forms hold the entire file in memory at once. A file you aren’t willing to hold in a Vec wants a chunked read off a File instead.

Here is the same Latin-1 file from the String page, through both readers:

Error { kind: InvalidData, message: "stream did not contain valid UTF-8" }
5 bytes read anyway

read_to_string has to produce a String, and a String can only hold valid UTF-8, so it reads the bytes and then checks them. The file was read fine — the error is about the encoding, not the disk, which is why the kind is InvalidData rather than something from the OS. fs::read skips the check, so an image, an archive, or a payload in a fixed-width encoding all come back the same way. Both readers enforce the same rule — a String can only hold valid UTF-8. One refuses the bytes; the other hands them over.

Bytes you’ve read can still be text — you only have to say so. String::from_utf8 takes the Vec<u8> and checks every byte; on failure the Vec comes back through into_bytes():

invalid utf-8 sequence of 1 bytes from index 3
[99, 97, 102, 233, 10]

The error names the byte where the text stops being valid — index 3, the lone 0xE9. from_utf8_lossy, str::from_utf8, and when to pick which are on the conversion page.