Introduction
I've been doubling down on Rust now for nearly 3 years. Some things with the language have only become clear to me in literally in the past couple of months. The notion of traits, for instance, and how to use them, has eluded me. It still does, but maybe the picture is becoming more clear.
Traits are essential. Everything in Rust that's important uses them, even the standard library.
Let's take a look at how we can:
- read the contents from a named file
- write those exact contents out to another file
- output the number of bytes that we wrote
Easy peasy stuff, let's go.
Back to Basics, Just Me and the Rust Compiler
We aren't going to use cargo
for this super simple code we're about to write. All we're going to do is use the rustc
compiler. To begin, make yourself a folder whereever you do your coding projects. I called mine io-concepts
. Then, change into that folder and do touch main.rs
(or ni main.rs
if in Powershell). This gets you a completely empty file to code in. Next, make a text file in the root of the same folder where you just created main.rs
, call it whatever you want. Put some content in it, whatever you want, this is our input file.
Next up what you just created in your editor of choice.
Type in this:
// main.rs
// dependencies
use std::fs::File;
use std::io::{Read, Result, Write};
// main function
fn main() -> Result<()> {
let mut input_file = File::open("input.txt")?;
let mut input_data = Vec::new();
input_file.read_to_end(&mut input_data)?;
println!("Input file contents represented as bytes: {:?}", input_data);
let mut output_file = File::create("output.txt")?;
output_file.write(&input_data)?;
println!("Wrote: {} bytes", input_data.len() * std::mem::size_of::<u32>());
Ok(())
}
If you typed that in, compile it by doing rustc main.rs
. Resolve any compiler errors that you might receive until there are none left. Run the resulting main.exe
(if on Windows) that you'll find in the project root and you should see a vector of bytes along with the number of bytes written to file.
Let's walk through it.
- We bring the
File
type from thestd::fs
module into scope, theFile
type affords us different methods to open and create files - the
Read
,Result
, andWrite
traits from thestd::io
module are brought into scope, affording us:- the ability to read file contents into a variable type of our choosing
- the Result type, which is a type alias for the
Result<T, E>
type available instd::io
- the ability to write contents back to a file
- it's important to note that these traits are already implemented for the
File
type will be using, we don't have to write their implementations from scratch
- we create a
main()
function, which is the starting point of our executable file, we make it return aResult<()>
type, which will be the unit type()
in the success cass or an error type - in our
main()
function we:- bind a file handle to a variable named
input_file
, this variable needs to be mutable - bind a new, empty vector to a variable named
input_data
- use the
read_to_end
method on our input_file handle, passing in a reference to theinput_data
type, which reads the contents of our file in as bytes - output a message, using the
println!()
macro, confirming we read the file and writing its contents back dostdout
- bind a file handle to a variable named
output_file
, this variable needs to be mutable - write the input data out, by calling the
write()
method and passing a mutable reference ot ourinput_data
- output a message, again using the
println!()
macro, confirming the number of actual bytes written
- bind a file handle to a variable named
- terminate by returning an
Ok(())
result, wrapping a unit value
You may be wondering why the input_file
and output_file
variables need to be mutable. In both cases, the acts of reading and writing change the respective files internal state, namely the position/cursor that's used to faciliate the internal location where to read and write to. This need to change state means the variable must be able to be changed, so it must be mutable.
That's it! I hope you've found benefit from this brief refresher on key aspects of file IO in Rust. Check out the std:fs
and std::io
documentation in the Rust standard library for more!