|

File IO in Rust

codeA guest post by Jeffery Olson, a software engineer currently living and working in the Seattle area. He’s still searching for that sweet spot between pragmatism and idealism. Besides contributions within the Rust community, he is interested in building performant HTML5 applications using emerging web technologies like WebGL and WebRTC. He can be reached at @olsonjeffery.

Rust is a programming language, sponsored by Mozilla and an active community of contributors. Its central concerns are:

This post will introduce Rust’s IO facilities, showing their use for basic filesystem operations. If you’re unfamiliar with the basic goals of Rust or its syntax, it’s recommended that you read the official Rust Tutorial, as this post assumes a passing familiarity with the language.

Appraising The Rust Runtime

Rust strives to provide a set of safe and robust APIs that serve tasks like network services or the need to interact with a user’s filesystem. The underlying code for this, combined with things like task-local-storage, messaging and scheduling primitives, comprise a Rust Runtime implementation. The rust codebase ships, by default, two Runtime implementations, distinguished around the issues of how IO is provided and how concurrency works. Very briefly:

Putting this aside, all of the IO APIs as presented in libstd (and this post) are agnostic to the underlying Runtime implementation. This provides the programmer with the flexibility to choose the right Runtime for their needs, even allowing a mix of Runtimes within a single process. Library maintainers writing Rust code that relies on the libstd APIs can do so knowing that their code will work in either environment.

Loading A File

Given a simple text file located in the same directory as where the program is executing from, here is a simple Rust snippet that will read the file and print it out for the user:

use std::str::from_utf8_owned;
use std::io::File;

fn main() {
    let path = Path::new("hw.txt");
    let mut hw_file = File::open(&path);
    let data = from_utf8_owned(hw_file.read_to_end());
    println!("{:s}", data);
}

The three use lines at the top are pulling in various bits of functionality needed to read from a file and parse it into a string.

Within the main() function, a Path is created pointing at the target file. It is then loaded with a call to File::open with a reference to path. Then the contents of the file are simultaneously read from the newly created hw_file with read_to_end and parsed to string format using from_utf8_owned. The result is stored in data.

Finally, the file is printed to stdout with the call to the println! macro. Since println! expects a static string, one is provided with a {:s} formatter along with the data value. More information about println! and format! can be found in the libstd docs for std::fmt.

Improvements And Variations

While useful for demonstration, it should be noted that read_to_end allocates and returns a ~[u8] byte vector. This approach is not very efficient in actual production scenarios where many reads would be happening in a main loop. More commonly, a programmer would stack-allocate a vector of empty bytes, and they would pass in a buffer slice (a reference to contiguous region of homogenous data, in-memory), which the File.read method would put the file’s data into. This buffer, provided it remains in scope, can be reused any number of times while the program is still reading input:

    let mut buf = [0u8, ..0x10000];
    match hw_file.read(buf) {
        Some(len) => {
            // do something with the data of len bytes
            // buf now contains the data from the read call
        }, None => {
            // EOF
        }
    }

This is the Rust version of the common “byte buffer” pattern in C et al for reading data. Of course, Rust’s approach has strong safety guarantees.

The hw_file representing the open file handle is explicity opened, but never closed. The simple explanation for this is that File implements the Drop trait, which is Rust’s way of providing destructor semantics. When the hw_file variable goes out of scope, the file handle underlying the File is closed out before the rest of its data is freed. When a programmer wishes to explicitly control a resource’s lifetime (for example: avoiding multiple handles to the same file simultaneously), they can be limited with scopes:

    let mut data = {
        let mut read_only = File::open(&path);
        from_utf8_owned(read_only.read_to_end())
    };
    // do some stuff and maybe mutate data
    {
        let mut write_only = File::open_mode(&path, Truncate, Write);
        write_only.write(data.as_bytes());
    }

It is also possible to explicitly invoke a value’s destructor and remove it from the current scope by calling drop(value). In the above example this could be done with drop(read_only), removing the need for explicit scopes.

And although read_only was opened read-only (the defined behavior when calling File::open), it was stored in a mutable variable (as seen in let mut read_only = ...). This is because, although the file itself is read-only, its internal position counter is changed on calls to read-related functions. In this regard, File has read(2)-like behavior.

Conclusion

More information on the std::io::fs module can be found in the libstd API docs, which provide a good overview of the module, as well some common use cases. Much of Rust’s IO API is defined in this manner: Expose simple APIs that leverage the strengths of the language’s type system to expose simple, safe and performant APIs to the end user.

For more details about general programming languages, see the Safari Books Online resources referenced below.

Not a subscriber? Sign up for a free trial.

Safari Books Online has the content you need

Practical Foundations for Programming Languages offers a fresh perspective on the fundamentals of languages through the use of type theory. Whereas most textbooks on the subject emphasize taxonomy, Harper instead emphasizes genetics, examining the building blocks from which all programming languages are constructed. Language features are manifestations of type structure. The syntax of a language is governed by the constructs that define its types, and its semantics is determined by the interactions among those constructs. The soundness of a language design – the absence of ill-defined programs – follows naturally.
Formal and Practical Aspects of Domain-Specific Languages cover how computer languages are a programmer’s basic tool and they play an essential role in computer science in which they specify computations which need to be performed as well as intended behavior of a system. Domain-Specific Language (DSL) is a particular computer programming language used to address a particular problem domain, representation technique, and solution technique. This book is a collection of academic works containing current research on all aspects of domain-specific language. This book is a comprehensive overview in the computer language field and aims to be essential for scholars and practitioners in the software engineering fields by providing new results and answers to open problems in DSL research.
Just Enough C/C++ Programming provides an intelligent layperson’s guide to programming. The book does not assume you are dimwitted, like so many other beginning programming texts do, but it doesn’t assume you are an engineer either. It simply gives you enough C/C++ language knowledge to be able to complete practical, industry-related, programming projects without becoming sidetracked. In short, it teaches you everything you need to know to be a successful programmer, without overloading you on the details.

About Safari Books Online

Safari Books Online is an online learning library that provides access to thousands of technical, engineering, business, and digital media books and training videos. Get the latest information on topics like Windows 8, Android Development, iOS Development, Cloud Computing, HTML5, and so much more – sometimes even before the book is published or on bookshelves. Learn something new today with a free subscription to Safari Books Online.
|

Comments are closed.