Chapter 18: Rust on your computer
This introduction explains why working on a computer offers capabilities beyond the Playground and identifies Cargo as Rust’s package manager.
After this chapter you will be able to
- Playground and local development. Explain why a Rust program may need to run on your computer rather than in the Playground, using examples from the section.
- External crates and Cargo. Describe the advantage of using external crates on your computer and state what Cargo is.
- Cargo’s common project structure. Explain that Cargo is used by nearly everyone to build and manage Rust projects, and that its common project structure makes it easier to work with external code written by multiple people.
- Cargo and rustc roles. Explain that rustc does the compiling, while Cargo uses rustc and automates the work of building a project, including bringing in external code.
- Dependencies and cargo run. Trace how the section adds rand to a Cargo project and what Cargo does when cargo run is used.
- Incremental Compilation Tradeoff. Explain why Rust’s ahead-of-time compilation takes time and how incremental compilation makes later builds faster after a change.
- Concrete Versions of Generic Functions. Describe how Rust compiles a generic function for the different concrete types used with it, and relate that compile-time work to runtime speed.
- Cargo Build Run and Check. Distinguish what cargo build, cargo run, and cargo check do, including when a quick compile check is useful.
- Debug and Release Build Tradeoff. Explain the difference between debug and release builds, using the section’s loop example to illustrate compile-time optimization.
- Two user input routes. Distinguish input supplied through stdin while a program is running from command-line arguments supplied before it runs.
- Reading keyboard input with stdin. Explain that std::io::stdin() provides a Stdin handle to keyboard input and that its .read_line() method reads into a mutable String.
- Line endings and trimmed input. Explain why typing x and pressing Enter does not produce exactly "x" for comparison, and how .trim() fixes that. Describe how {:?} helps reveal the extra characters.
- Command-line argument iteration. Explain what std::env::args() provides and how to process its arguments with a for loop, skipping the program name when it is not needed.
- Arguments as program instructions. Describe how the example uses a command-line argument to choose what to do with later words, and what happens when the choice is unrecognized or no later words are supplied.
- Inspecting and setting environment variables. Describe how to view environment variables with std::env::vars() and set one while a program is running with std::env::set_var().
- Logging configuration fallback. Trace how the example chooses a RUST_LOG value when one has not already been set.
- File errors and Results. Explain why Rust code that works with files often uses Results and finds the ? operator handy, giving examples of what can go wrong.
- Creating and writing files. Explain that std::fs provides file operations and that bringing std::io::Write into scope lets you use .write_all() to write data to a file. Describe how the question mark operator can shorten code by passing on a successful result.
- Convenient whole-file writing. Describe how std::fs::write() takes a file name and content to write in one call, accepts a &str without a b prefix, and deletes the existing contents if that file already exists.
- Opening and reading existing files. Explain that opening an existing file uses open() instead of create(), and that read_to_string() can read its contents into a String if the file is found.
- OpenOptions file configuration. Describe how OpenOptions configures actions such as reading, writing, and creating a file before .open() is called with a filename.
- Existing file safeguards. Distinguish .create_new(), .append(), and .truncate() by what each does when working with file contents or an existing filename.
- Generating local documentation. Explain that `cargo doc` automatically generates documentation in a consistent format, and describe what `--open` and `--no-deps` change.
What it builds on
- Crates and Cargo.toml (chapter 16) · Introduces external crates and Cargo.toml.
- Generics (chapter 5) · generic functions and types
- Mutable references (chapter 2) · Mutable references, used here as &mut String.
- Display and Debug (chapter 1) · Display and Debug printing, used here to inspect the input with {:?}.
- Iterators (chapter 8) · The idea of processing items with an iterator.
- Result (chapter 5) · The Result type
- The ? operator (chapter 6) · The ? operator
- The builder pattern (chapter 15) · The builder pattern used to set options.
Section by section
Introduction
This introduction explains why working on a computer offers capabilities beyond the Playground and identifies Cargo as Rust’s package manager.
- Playground and local development: Explain why a Rust program may need to run on your computer rather than in the Playground, using examples from the section.
Why it matters: It helps explain why the chapter moves from the Playground to Rust on your computer. - External crates and Cargo: Describe the advantage of using external crates on your computer and state what Cargo is.
Why it matters: Access to more external crates is the introduction’s main reason for using Rust on your computer.
Key terms: Playground, user input, command-line arguments, external crates, Cargo, package manager
18.1 Cargo
Introduces Cargo as the widely used tool that gives Rust projects a common structure for building and managing them.
- Cargo’s common project structure: Explain that Cargo is used by nearly everyone to build and manage Rust projects, and that its common project structure makes it easier to work with external code written by multiple people.
Why it matters: This explains why Cargo is so common in Rust projects.
Key terms: Cargo, common structure, external code
18.1.1 Why everyone uses Cargo
This section shows why Rust projects use Cargo instead of invoking rustc directly, then demonstrates how Cargo creates projects and builds programs with external crates.
- Cargo and rustc roles: Explain that rustc does the compiling, while Cargo uses rustc and automates the work of building a project, including bringing in external code.
Why it matters: This distinction explains why you normally build a Rust project with Cargo even though rustc is the compiler. - Cargo project creation: Describe how to create a Cargo project and where its starting code goes, including the difference when creating a library.
Why it matters: Knowing the generated layout tells you where to put code and where to declare a dependency. - Dependencies and cargo run: Trace how the section adds rand to a Cargo project and what Cargo does when cargo run is used.
Why it matters: It explains why the build can show more crates than the one you explicitly added.
Key terms: rustc, Cargo, crate, cargo new, Cargo.toml, src, main.rs, --lib, lib.rs, rand
18.1.2 Using Cargo and what Rust does while it compiles
This section explains why Rust compilation takes time, how Cargo supports development, and how debug and release builds trade compile time for runtime speed.
- Incremental Compilation Tradeoff: Explain why Rust’s ahead-of-time compilation takes time and how incremental compilation makes later builds faster after a change.
Why it matters: It explains why a slow first build need not mean every edit will be slow. - Concrete Versions of Generic Functions: Describe how Rust compiles a generic function for the different concrete types used with it, and relate that compile-time work to runtime speed.
Why it matters: It gives a concrete example of work Rust does during compilation to make the finished program faster. - Cargo Build Run and Check: Distinguish what cargo build, cargo run, and cargo check do, including when a quick compile check is useful.
Why it matters: Choosing the command that matches your task avoids waiting for a build when you only need a check. - Debug and Release Build Tradeoff: Explain the difference between debug and release builds, using the section’s loop example to illustrate compile-time optimization.
Why it matters: It helps you choose a quick build while developing and a more optimized build for finished code. - Additional Cargo Command Purposes: State what cargo clippy, cargo build --timings, cargo clean, cargo add, and cargo doc are used for.
Why it matters: These commands help with common project tasks beyond building and running code.
Key terms: ahead of time, incremental compilation, external crate, generic function, concrete, specialized definition, monomorphization, cargo build, cargo run, cargo check
18.2 Working with user input
Introduces two ways a Rust program can receive user input and distinguishes when each is supplied.
- Two user input routes: Distinguish input supplied through stdin while a program is running from command-line arguments supplied before it runs.
Why it matters: The timing of the input helps you tell these two routes apart.
Key terms: stdin, command line arguments, user input
18.2.1 User input through stdin
This section shows how to read keyboard input with stdin and why trimming that input matters when comparing it with a typed character.
- Reading keyboard input with stdin: Explain that std::io::stdin() provides a Stdin handle to keyboard input and that its .read_line() method reads into a mutable String.
Why it matters: This is the section’s basic way to get input typed by a user. - Line endings and trimmed input: Explain why typing x and pressing Enter does not produce exactly "x" for comparison, and how .trim() fixes that. Describe how {:?} helps reveal the extra characters.
Why it matters: It explains why input that looks like x on screen may fail a comparison with "x".
Key terms: std::io::stdin, Stdin, read_line, &mut String, {:?}, .trim(), whitespace, \r\n, \n
18.2.2 Accessing command-line arguments
This section shows how a Rust program reads command-line arguments, skips its own name, and uses the remaining strings as input.
- Command-line argument iteration: Explain what std::env::args() provides and how to process its arguments with a for loop, skipping the program name when it is not needed.
Why it matters: It lets a program handle what the user typed at startup without mistaking its own name for user input. - Arguments as program instructions: Describe how the example uses a command-line argument to choose what to do with later words, and what happens when the choice is unrecognized or no later words are supplied.
Why it matters: Startup arguments can tell a program which action to take, not just provide text to print. - CLI argument conventions: Explain the section’s example of a command-line option and when it recommends using clap.
Why it matters: It connects the small argument example to how larger command-line tools accept commands and flags.
Key terms: std::env::Args, std::env::args(), command-line arguments, IntoIterator, .skip(), index 1, capital, lowercase, match, CLI
18.2.3 Accessing environment variables
This section shows how to inspect and set environment variables, then uses logging settings to illustrate a practical fallback.
- Inspecting and setting environment variables: Describe how to view environment variables with std::env::vars() and set one while a program is running with std::env::set_var().
Why it matters: These functions let a program read its settings and adjust them while it runs. - Logging levels and environments: Explain what RUST_LOG controls and why a development deployment might use a different logging level from production.
Why it matters: The example uses logging levels to show why a program might choose settings based on its environment. - Logging configuration fallback: Trace how the example chooses a RUST_LOG value when one has not already been set.
Why it matters: This example shows how a program can choose a sensible setting when an explicit one is missing.
Key terms: environment variables, std::env::vars(), std::env::set_var(), key, value, RUST_LOG, TRACE, DEBUG, INFO, WARN
18.3 Using files
Introduces file work in Rust as an error-prone activity where Results and the ? operator are useful.
- File errors and Results: Explain why Rust code that works with files often uses Results and finds the ? operator handy, giving examples of what can go wrong.
Why it matters: Expecting file operations to fail helps explain why the following file examples use error-handling tools.
Key terms: Results, ? operator, files
18.3.1 Creating files
Introduces writing data to a new file, then shows shorter ways to do it and warns that std::fs::write replaces existing contents.
- Creating and writing files: Explain that std::fs provides file operations and that bringing std::io::Write into scope lets you use .write_all() to write data to a file. Describe how the question mark operator can shorten code by passing on a successful result.
Why it matters: These are the pieces the section uses to create a file and put data in it. - Convenient whole-file writing: Describe how std::fs::write() takes a file name and content to write in one call, accepts a &str without a b prefix, and deletes the existing contents if that file already exists.
Why it matters: The one-call option is convenient, but its effect on an existing file is important to remember.
Key terms: std::fs, std::io::Write, .write_all(), question mark operator, std::fs::write(), &str, AsRef<[u8]>
18.3.2 Opening existing files
This subsection explains how to open an existing file and read its contents into a String.
- Opening and reading existing files: Explain that opening an existing file uses open() instead of create(), and that read_to_string() can read its contents into a String if the file is found.
Why it matters: It connects opening a file with the next step of getting its contents into a String.
Key terms: open(), create(), read_to_string(), String
18.3.3 Using OpenOptions to work with files
This section shows how to choose file-opening behavior with OpenOptions and explains the tradeoff between embedding a file with include_str! and reading it at run time.
- OpenOptions file configuration: Describe how OpenOptions configures actions such as reading, writing, and creating a file before .open() is called with a filename.
Why it matters: It lets you choose how a file should be handled instead of relying on a fixed way of opening it. - Existing file safeguards: Distinguish .create_new(), .append(), and .truncate() by what each does when working with file contents or an existing filename.
Why it matters: Choosing the wrong option can delete contents you meant to keep. - Embedded file content tradeoff: Explain how include_str! differs from reading a file at run time with std::fs::read_to_string().
Why it matters: The choice affects when a missing file is caught and whether its contents increase the binary's size.
Key terms: OpenOptions, File::options(), .open(), Result, builder pattern, .create_new(), .append(), .truncate(), include_str!, &'static str
18.4 cargo doc
This section shows how `cargo doc` generates browsable Rust documentation, including trait information and documentation comments.
- Generating local documentation: Explain that `cargo doc` automatically generates documentation in a consistent format, and describe what `--open` and `--no-deps` change.
Why it matters: You can browse your project’s documentation alongside documentation for its dependencies without searching separate websites. - Information in generated pages: Describe what the generated pages can show for even a tiny project, including methods and traits not explicitly implemented in its code.
Why it matters: The generated page can reveal capabilities that are not obvious from the code you wrote. - Documentation comments: Explain how `///` comments above structs and methods appear in documentation generated by `cargo doc`.
Why it matters: A short comment in your code can become an explanation for readers of its documentation.
Key terms: cargo doc, --open, --no-deps, automatically implemented, blanket implementations, methods, documentation comments, ///
18.5 Summary
Where this is used later
- Developing the code (chapter 23) · Reading an existing file into a String.
Concepts explained in this chapter
- CargoCargo is Rust's package manager and build tool for creating, configuring, compiling, running, and managing Rust projects and their dependencies.
- cargo runcargo run is a Cargo subcommand that builds and runs a Rust binary target while resolving its dependencies.
- Compiler ExplorerGodbolt is an online compiler-explorer service that displays compiler-generated assembly for source code under selectable compiler and optimization settings.
- cargo checkcargo check is a Cargo command that analyzes a Rust package for compilation errors without producing a runnable executable.
- cargo buildcargo build is a Cargo command that compiles a Rust program into an executable artifact.
- monomorphizationMonomorphization generates a separate concrete function or type implementation for each concrete type used with generic code.
- OpenOptionsOpenOptions is a Rust standard-library builder type for configuring how a filesystem file is opened or created.
- cargo doccargo doc is the Cargo command-line tool that generates documentation for Rust packages and their dependencies, with --open optionally opening the generated documentation.
- command-line argumentA command-line argument is a value supplied to a program when it is launched through a command-line interface.
- environment variableAn environment variable is a named operating-system or process setting represented as a string key and string value.
- read_to_stringread_to_string is a filesystem operation that reads a file's contents into a String during program execution.
- cargo.tomlCargo.toml is the manifest file that declares a Rust package's metadata and dependencies, including external crates and their versions.
- commentA comment is source text that the compiler ignores as program instructions and that can contain explanatory or documentation text.
- documentationDocumentation is explanatory material that describes how code works and how it can be used.
- question mark operatorThe question mark operator is Rust syntax that propagates an error from a Result or absence from an Option.
- result typeResult is a Rust enum type representing either a successful value or an error from an operation that may fail.
- cargo addcargo add is a Cargo command that adds an external crate dependency to a package manifest.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
