Programming With Rust Donis Marshall All Chapter
Programming With Rust Donis Marshall All Chapter
Donis Marshall
Many of the designations used by manufacturers and sellers to distinguish
their products are claimed as trademarks. Where those designations appear
in this book, and the publisher was aware of a trademark claim, the
designations have been printed with initial capital letters or in all capitals.
The author and publisher have taken care in the preparation of this book,
but make no expressed or implied warranty of any kind and assume no
responsibility for errors or omissions. No liability is assumed for incidental
or consequential damages in connection with or arising out of the use of the
information or programs contained herein.
For information about buying this title in bulk quantities, or for special sales
opportunities (which may include electronic versions; custom cover
designs; and content particular to your business, training goals, marketing
focus, or branding interests), please contact our corporate sales department
at [email protected] or (800) 382-3419.
Hoboken, NJ
FIG22-01: Microsoft
All rights reserved. This publication is protected by copyright, and
permission must be obtained from the publisher prior to any prohibited
reproduction, storage in a retrieval system, or transmission in any form or
by any means, electronic, mechanical, photocopying, recording, or likewise.
For information regarding permissions, request forms and the appropriate
contacts within the Pearson Education Global Rights & Permissions
Department, please visit www.pearson.com/permissions.
ISBN-13: 978-0-13-788965-5
ISBN-10: 0-13-788965-8
$PrintCode
Pearson’s Commitment to Diversity, Equity, and
Inclusion
Pearson is dedicated to creating bias-free content that reflects the diversity
of all learners. We embrace the many dimensions of diversity, including but
not limited to race, ethnicity, gender, socioeconomic status, ability, age,
sexual orientation, and religious or political beliefs.
Education is a powerful force for equity and change in our world. It has
the potential to deliver opportunities that improve lives and enable
economic mobility. As we work with authors to create content for every
product and service, we acknowledge our responsibility to demonstrate
inclusivity and incorporate diverse scholarship so that everyone can achieve
their potential through learning. As the world’s leading learning company,
we have a duty to help drive change and live up to our purpose to help more
people create a better life for themselves and to create a better world.
Our ambition is to purposefully contribute to a world where:
Everyone has an equitable and lifelong opportunity to succeed
through learning.
Our educational products and services are inclusive and represent the
rich diversity of learners.
Our educational content accurately reflects the histories and
experiences of the learners we serve.
Our educational content prompts deeper discussions with learners and
motivates them to expand their own learning (and worldview).
While we work hard to present unbiased content, we want to hear from
you about any concerns or needs with this Pearson product so that we can
investigate and address them.
Please contact us with concerns about any potential bias at
https://www.pearson.com/report-bias.html.
Contents
1 Introduction to Rust
Introduction
Functional Programming
Expression Oriented
Pattern-Oriented
Features
Safeness
Ownership
Lifetimes
Fearless Concurrency
Zero-Cost Abstraction
Rust Terminology
Tools
A Note About Security
Summary
2 Getting Started
Preliminaries
Rust and Windows
Installing Rust
Advanced Rustup
“Hello, World”
Compile and Run
Cargo
Library
Comments
Published Crates
Main Function
Command-Line Arguments
Summary
3 Variables
Terminology
Variables
Primitives
Integer Types
Overflow
Notations
Floating Point Types
Floating Point Constants
Infinity
NaN
Numeric Ranges
Casting
Boolean Types
Char
Pointers
References
Operators
Summary
4 Strings
Str
String
Length
Extending a String
Capacity
Accessing a String Value
String Characters
Deref Coercion
Formatted String
Helpful Functions
Summary
5 Console
Print
Positional Arguments
Variable Arguments
Named Arguments
Padding, Alignment, and Precision
Base
Developer Facing
Write! Macro
Display Trait
Debug Trait
Format! Macro
Console Read and Write
Summary
6 Control Flow
The if Expression
The while Expression
The break and continue Keywords
The for Expression
The loop Expression
The loop break Expression
The loop Label
The Iterator Trait
Summary
7 Collections
Arrays
Multidimensional Arrays
Accessing Array Values
Slices
Comparing Arrays
Iteration
Coercion
Vectors
Multidimensional
Access
Iteration
Resizing
Capacity
HashMap
Creating a HashMap
Accessing the HashMap
Updating an Entry
Iteration
Summary
8 Ownership
Stack and Heap Memory
Shallow versus Deep Copy
Car Analogy
Move Semantics
Borrow
Copy Semantics
Clone Trait
Copy Trait
Clone Trait
Summary
9 Lifetimes
Introduction to Lifetimes
Function Headers and Lifetimes
Lifetime Annotation
Lifetime Elision
Complex Lifetimes
Sharing a Lifetime
Static Lifetimes
Structs and Lifetimes
Methods and Lifetimes
Subtyping Lifetimes
Anonymous Lifetimes
Generics and Lifetimes
Summary
10 References
Declaration
Borrowing
Dereferencing
Comparing References
Reference Notation
Reference to Reference
Mutability
Limits to Multiple Borrowers
Summary
11 Functions
Function Definition
Parameters
Function Return
Const Functions
Nested Functions
Function Pointers
Function Aliases
Summary
12 Error Handling
Handling Error Handling
The Result Enum
The Option Enum
Panics
Panic! Macro
Handling Panics
Unwrapping
Match Pattern for Result and Option
Map
Rich Errors
Custom Errors
Summary
13 Structures
Alternate Initialization
Move Semantics
Mutability
Methods
Self
Associated Functions
Impl Blocks
Operator Overloading
Unary Operator Overloading
Binary Operator Overloading
Tuple Struct
Unit-Like Struct
Summary
14 Generics
Generic Functions
Bounds
The where Clause
Structs
Associated Functions
Enums
Generic Traits
Explicit Specialization
Summary
15 Patterns
Let Statement
Wildcards
Complex Patterns
Ownership
Irrefutable
Ranges
Multiple Patterns
Control Flow
Structs
Functions
Match Expressions
Match Guards
Summary
16 Closures
“Hello, World”
Closure Syntax
Closed Over
Closures as Function Arguments
Closures as Function Return Values
Implementation of Closures
The Fn Trait
The FnMut Trait
The FnOnce Trait
The move Keyword
The Impl Keyword
Matrix Example
Summary
17 Traits
Trait Definition
Default Functions
Marker Trait
Associated Functions
Associated Types
Extension Methods
Fully Qualified Syntax
Supertraits
Static Dispatch
Dynamic Dispatch
Enums and Traits
Summary
18 Threads 1
Synchronous Function Calls
Threads
The Thread Type
Processor Time
Builder
Communicating Sequential Process
Asynchronous Channel
Synchronous Channel
Rendezvous Channel
The try Methods
Store Example
Summary
19 Threads 2
Mutex
Nonscoped Mutex
Mutex Poisoning
Reader-Writer Lock
Condition Variables
Atomic Operations
Store and Load
Fetch and Modify
Compare and Exchange
Summary
20 Memory
Stacks
Static Values
The Heap
Interior Mutability
RefCell
OnceCell
Summary
21 Macros
Tokens
Declarative Macros
Repetition
Multiple Macro Matchers
Procedural Macros
Derive Macros
Attribute Macros
Function-Like Macros
Summary
22 Interoperability
Foreign Function Interface
Basic Example
Libc Crate
Structs
Bindgen
C Calling Rust Functions
Cbindgen
Summary
23 Modules
Module Items
Module Files
The path Attribute
Functions and Modules
The crate, super, and self Keywords
Legacy Model
Summary
Index
Register your copy of Programming with Rust on the InformIT site for
convenient access to updates and/or corrections as they become
available. To start the registration process, go to informit.com/register
and log in or create an account. Enter the product ISBN
(9780137889655) and click Submit. Look on the Registered Products
tab for an Access Bonus Content link next to this product, and follow
that link to access any available bonus materials. If you would like to be
notified of exclusive offers on new editions and updates, please check
the box to receive email from us.
About the Author
Introduction
Rust is a general-purpose language for creating safe, secure, and scalable
applications. The language has features from several programming
paradigms, as described shortly. Rust was originally designed as a systems
programming language. However, it has emerged as a more versatile
language capable of creating a variety of application types, including
systems programming, web services, desktop applications, embedded
systems, and more. Although it may sound cliché, what you can accomplish
with Rust is only limited by your imagination.
Different! That is an accurate assessment of Rust. Although the Rust
syntax is based on the C and C++ languages, the similarity with other C-
based languages often ends there. In addition, Rust is not different just to be
different; it is a difference with a purpose.
Rust’s borrow checker is an excellent example of a difference with a
purpose. The borrow checker is a unique feature within Rust that promotes
safe coding practices by enforcing rules related to the single ownership
principal. No other language has this feature. For that reason, the borrow
checker is a foreign concept to many developers but nonetheless invaluable.
In many ways, Rust represents lessons learned. Some of the unique
features in Rust are lessons learned, or instances of a lack of success, from
other languages. The willingness of Rust to depart from the normal script
when necessary is a major feature of the language. Many programming
languages struggle with effective memory management, for example. That
is the purpose of the ownership feature in Rust—effective memory
management.
Let’s be candid for a moment. Learning Rust can be frustrating at times.
It requires an investment of time and the sacrifice of some brain cells.
However, you will find that your investment in learning Rust is more than
worthwhile. For example, learning to work with the borrow checker, not
against it, is invaluable.
My goal in writing this book is to increase the number of Rustaceans—
individuals with a high proficiency in the Rust programming language. My
hope is that you will become an active member of the Rust community with
your new mastery of the language. You have now officially started the
journey toward becoming a Rustacean.
Functional Programming
Rust embraces a variety of programming paradigms, including functional
programming, expression-oriented programming, and pattern-oriented
programming. Let’s explore these various paradigms, starting with
functional programming. Since Rust is our focus, I will present just a
review of these programming models in this book.
What is functional programming? It is a programming model where
functions are the essential building blocks of the language. With functional
programming, functions are first-class citizens. You can use functions
wherever a variable is normally found: a local variable, function parameter,
or as a function return. A function can even perform operations on other
functions, described as a higher-order function.
Rust is functional programming light. The language does not include
every feature found in functional programming, such as lazy evaluation, a
declarative programming style, tail call optimization, and more. However,
Rust does support a functional style of programming.
Functional programming languages typically restrict procedural
programming capabilities, such as global functions. As they are not
incongruent, Rust allows for the comingling of procedural- and functional-
style programming.
Pure functions are the centerpiece of functional programming. As a pure
function, a function is fully described through its function interface. There
is a direct correlation between the function parameters and a specific return
value, without side effects. In addition, the results of a pure function should
be repeatable. For example, a function that relies on an internal random
number, making the results unpredictable, is not a pure function.
Immutability is an important ingredient of functional programming,
which is a core tenet of Rust. Pure functions, for example, lean heavily on
immutable state to eliminate side effects. Pointers, global variables, and
references are generally omitted from a pure function to avoid side effects
that can leak from a function.
To summarize, functional programming offers several benefits:
Added flexibility, with functions as first-class citizens
More transparency, with the focus on functions and not individual
lines of code
Immutability, which makes programs easier to maintain by removing
common problems such as side effects within functions
Expression Oriented
Rust is also an expression-oriented language, which is a programming style
where most operations are expressions that return a value, instead of
statements that return nothing. Expression-oriented programming is a close
cousin of functional programming. All functional programming languages
are also expression-oriented languages.
So what is a statement, and what is an expression?
Statements do not return a value, but they can cause a side effect. The
possible side effects are unlimited, including database manipulation
or updating a shared variable. Indeed, the purpose of some statements
is the side effect.
Expressions are one or more operations that return a value, with
minimal or no side effects. Pure functions are examples of
expressions.
In Rust, expressions are preferred, even transfer of control statements
such as the if and while statements are actually expressions.
The many benefits of expression-oriented programming include the
following:
Without side effects, expression-oriented programs are easier to
maintain.
The value of an expression is fully defined through its interface. This
makes expressions more transparent.
Because expressions are interface driven, they are more testable.
Expression-oriented programming makes documentation easier.
Without side effects, the expression can substitute as the
documentation.
Expressions are more easily composed.
The ability to combine the various programming paradigms is another of
Rust’s advantages.
Listing 1.1 provides an example of both functional and expression-
oriented programming in Rust. The factorial function is a pure function with
no side effect. As an expression, the factorial function returns the result of
the factorial calculation.
Pattern Oriented
Typically, professional programming in Rust involves an abundance of
patterns. Fair to say, patterns have a ubiquitous presence in Rust coding.
This nod to pattern matching contributes to the uniqueness of the Rust
programming style. Rust source code simply looks different from C++ or
Java, for example!
Pattern matching is often associated with switch statements. For
example, C++, Java, Go, and other languages have a switch statement. This
is single dimensional pattern matching largely based on string or integral
expressions. In Rust, pattern matching is extended to instances of user-
defined types and sequences.
Rust has a match expression instead of a switch statement. However,
pattern-oriented programming extends well beyond the match expression.
Every instance of any expression in Rust is an opportunity for pattern
matching. For example, even a simple assignment or conditional expression
can result in pattern matching. This provides interesting ways of
reimagining your code.
Pattern-oriented programming in Rust offers several benefits:
Patterns in Rust are highly expressive. This allows you to collapse
complex code into simpler expressions.
In Rust, pattern-oriented programming is a complementary model to
expression-oriented programming.
Rust supports exhaustive pattern matching, which is more dependable
and less error prone.
The display_firstname function demonstrates pattern matching, as
shown in Listing 1.2. The function parameter is name, which is a tuple. The
fields of the tuple are last and first. In the match expression, patterns are
used to destructure the tuple, extract the first name, and print the name.
Features
Professionals regularly select Rust as their favorite programming language
in a variety of recent surveys. Much of this recognition pertains to the
unique features found in Rust.
Let’s explore the core features that contribute to Rust and its popularity.
Safeness
Safeness is an important crosscutting feature touching almost all aspects of
the language. Safe code is robust, predictable, and not prone to unexpected
errors. With these attributes, Rust provides a solid foundation where you
can confidently develop applications. Immutable variables, the single
ownership principle, and other features contribute to this objective.
In addition, Rust enforces safe coding practices at compile time. The
ownership model with the borrow checker is a perfect example of this
approach. At compile time, the borrow checker performs a series of checks,
including ownership. If the ownership check fails, the borrow checker
presents an explanation and the compilation does not complete successfully.
Several factors contribute to safe code in Rust:
Immutability is the default, which prevents inadvertent changes
The enforcement of proper lifetimes to prevent anti-patterns, such as
dangling references
References for safe pointers
A “resource acquisition is initialization” (RAII) strategy for variably
sized resources, such as vectors, for dependable memory
management
Ownership
The ownership feature provides safe memory access using the single owner
principle. This principle consigns a single owner to variables, and never
more than one owner.
This approach prevents sharing ownership of the same memory. Race
conditions, unstable variables, and dangling references are some of the
potential problems mitigated with this approach. There are exceptions for
sharing ownership as presented later in this book.
Let’s use a car analogy to demonstrate the single owner principle. Here
are the basic facts: There is a car, and Bob is its sole owner.
Now here are two scenarios:
Bob has the car.
Ari occasionally wants to drive the same car.
As the owner, Bob can always drive the car, except when he has loaned
it to someone else. If someone else (Ari) wants to drive the car, there are
two possibilities: Bob must either sell or lend the vehicle to Ari. Either way,
Bob loses possession of the vehicle, at least temporarily.
Here are the steps if Bob lends Ari the car.
Bob drives the car.
Bob lends the car to Ari. Ari drives the car. When Ari is done, he
returns the car to Bob.
Bob drives the car.
The borrow checker is responsible for enforcing correct ownership,
including lending, at compile time. We will unlock the mystery of the
borrow checker later in the book with the goal of making the borrow
checker your best friend.
Lifetimes
Lifetimes is a feature in Rust that prevents accessing values that are no
longer available. A reference is a basic pointer in Rust. If allowed, improper
access to dropped values can cause hanging references and potential
program failure. The result would be vulnerable applications that are both
unstable and unpredictable. Rust eliminates this problem with the lifetimes
model.
The borrow checker manages lifetimes. You are notified at compile time
of invalid lifetimes. These sorts of problems are better isolated at compile
time, not at runtime.
When there is ambiguity related to lifetimes, lifetime annotations are
hints to the borrow checker about the proper lifetime. If determining the
lifetime is trivial, lifetime annotations are not required. The borrow checker
will just know. This is called lifetime elision.
The benefit of the lifetimes feature is a stable memory environment
without the worry of hanging references.
Fearless Concurrency
Fearless concurrency is important and worthy of inclusion in the list of
major Rust features. Fearless concurrency provides a safe environment for
concurrent programming. In many ways, this safe environment is created
from the benefit of the beforementioned features. For example, the
ownership model in Rust largely eliminates race conditions in concurrent
programming.
When transitioning from sequential to concurrent programming a
process called hardening is often undertaken to ensure a safe environment
for multithreaded code. Removing global variables, as shared data, is
typically one step in the hardening process. Fearless concurrency eliminates
the need for hardening.
Concurrent programming is often considered the bogeyman of coding. It
can add complexity and make applications less maintainable. Worst of all,
problems in concurrent programming are often not found until runtime.
Fearless concurrency creates a safer environment for concurrent
programming.
Zero-Cost Abstraction
Zero-cost abstraction is a feature of Rust features. Yes, you read that
correctly. For that reason, it is the final feature mentioned here. Zero-cost
abstraction is the policy that Rust features should not incur a performance
penalty at runtime, if avoidable.
Generational garbage collection is an intrinsic feature of several popular
managed languages, including Java, C#, and Go, for managing dynamic
memory. Garbage collection can be costly and nondeterministic. As such,
you never know when garbage collection may occur. Compare this to Rust,
where there is no memory model. Absolutely none! Ownership, as
described earlier in this chapter, provides deterministic memory
management without overhead. That is an example of zero-cost abstraction.
Rust Terminology
Many technologies, including programming languages, have their own
terminology. Familiarizing yourself with that terminology can be helpful
when communicating with peers and others in the larger Rust community.
For Rust, the motif is crates, as in shipping crates. Here are some of the
important terms in Rust. These will help you talk Rust.
Rust: Let’s start with the most important term: “Rust” itself. Rust is
not an acronym or a special technology term. The name Rust comes
from the term rust fungi, which is a robust pathogen that attacks
living plants.
Graydon Hoare, the original Rust designer, has been credited with
this statement: “I think I named it after fungi. Rusts are amazing
creatures.”
Crate: A crate is a compilation unit in Rust. Executable, library, or
external crates are the most common.
Executable crate: An executable crate is a binary executable that can
be launched independently of other crates.
Library crate: Library crates provide services to other crates and do
not execute independently.
External crate: External crates are external dependencies. For
example, Crate A references Crate B, but Crate B is not within the
same package. Therefore, Crate B is an external crate, or dependency,
for Crate A.
Packages: A package consists of multiple crates that provide a
specific service. Packages can consist of multiple executable crates
and possibly a single library crate.
Modules: Modules in Rust are similar to namespaces from other
programming languages. You can use modules to create a hierarchical
program structure within a crate. Modules also help to avoid name
collisions.
Cargo: There are several cargo entities within Rust, which extends
the crates motif (cargo in crates).
Cargo tool: The Cargo tool is the Rust package manager.
Cargo.toml: The Cargo.toml file is the manifest and configuration
file for Rust.
Cargo.lock: The Cargo.lock file is a record of all dependencies with
their specific versions.
RS: RS (Rust source) is the extension for Rust source files.
Figure 1.1 shows the interrelationship of various elements of Rust.
Figure 1.1 The interrelationship of various elements in Rust
Tools
The Rust environment hosts a variety of tools that span a variety of
services, from compiling Rust source code to publishing crates.
Understanding these tools will improve your productivity. There are too
many tools to list here, and more tools will be introduced throughout the
book.
This is a list of the more important Rust tools:
Rustup tool: The Rustup tool is the Rust installer. It also installs the
toolchain. You can download the Rust installer at https://rustup.rs.
Follow the directions there to successfully install Rust.
Cargo tool: Cargo is a multipurpose tool with package manager as
the primary role. Ancillary services include compiling code,
formatting source code, and creating new crates.
Here is a Cargo statement that creates a library crate:
cargo new --lib mylib
Rustc tool: Rustc is the Rust compiler. Rustc can compile a Rust
source file (.rs) into an executable or library binary.
Here is the Rustc statement to build a simple crate:
$ rustc source.rs
Summary
This chapter introduced the software paradigms associated with Rust and
many of its notable features.
The availability of the various paradigms provides flexibility and a best-
of-all-worlds approach. The following Rust features complement the
various programming models and define the important attributes of the
language:
Safeness
Ownership
Lifetimes
Trustworthy concurrency
Zero-cost abstraction
You now have a foundation in Rust, including its toolchain. In the next
chapter, you will complete your first Rust application and further explore
important tools, such Rustc and Cargo.
2
Getting Started
This chapter introduces the core concepts required to create, compile, and
run Rust executables and libraries. This includes completing your first Rust
application—the illustrious “Hello, World” application. When learning a
programming language, this application is often the first example. It is a
great tool for learning a new programming language. The first documented
example of this familiar application is found in the book The C
Programming Language from Brian Kernighan and Dennis Ritchie back in
1972. Who are we to break tradition?
Several variations of the “Hello, World” application will be presented
here while we explore different “getting started” topics.
We will also continue exploring the Rust toolchain in this chapter.
Knowledge of the tools in the toolchain, such as Cargo and Rustc, is
essential to productive interaction with the Rust environment. For this
reason, components of the toolchain are introduced here.
Preliminaries
Before we create our first application, Rust must be installed. Rustup is the
Rust installer and toolchain manager. Rustup manages the proper
installation for specific platforms.
By default, Rustup installs the latest stable build, also called a channel.
At the moment, Rust is on a six-week release cycle. The upcoming release
schedule is published at Rust Forge (https://forge.rust-lang.org/). The
following are the three available channels:
Stable: The latest release
Beta: The next version available with the upcoming release
Nightly: The nightly build, which includes experimental features
You can also request that Rustup install specific versions of the Rust
environment. This is especially useful if your organization has not upgraded
to the latest release.
Different methods are available to install Rust. Each requires a different
level of user involvement, from minimal to moderate. For a standard
installation, choose a minimal approach. However, more involvement is
required if you want to customize the installation.
Installing Rust
The most straightforward approach to installing the Rust language and
toolchain is to visit the Rustup website. This is the minimal approach for a
quick install of the standard Rust environment. You are presented with
minimal options and documentation. Here is the web location:
https://rustup.rs
Alternatively, visit the Install Rust web page for more documentation
and options, such as selecting either a 32-bit or 64-bit installation. Here is
the location of the Install Rust page:
www.rust-lang.org/tools/install
For even more details, visit the Rust Getting Started page. This page
documents how to install Rust, with some options, and it provides some
helpful “getting started” commands for the Cargo tool, the Rust build tool
and package manager. The bottom of the web page lists various editors and
integrated development environments (IDEs) available to Rust developers:
www.rust-lang.org/learn/get-started
Rustup installs the Rust toolchain locally in these directories:
Windows: \users\{user}\.cargo\bin
Linux: home/.cargo/bin
macOS: /users/{user}/.cargo/bin
A great practice is to trust but verify. This would include your Rust
installation. This is easily accomplished with either the rustc or the cargo
tool, both included in the newly installed Rust toolchain. From an operating
system command line, enter these commands:
rustc --version
cargo --version
$ rustc --version
rustc 0.00.0 (a8314ef7d 0000-00-00)
Advanced Rustup
As demonstrated, rustup is an excellent tool for the general installation of
Rust. It can also install a specific version of the Rust environment. This
could be advantageous for various reasons, such as product requirements,
reproducing problems found in older builds, or maybe developing on a code
branch based from an earlier version of Rust. The rustup install version
command installs the indicated version of Rust. For example, Rust 1.34.2 is
a version from way back in 2019. Assuming this version is required for
some reason, rustup can install that specific version, as follows:
$ rustup install 1.34.2
You can also install Rust from one of three channels, as mentioned
earlier. The default channel is the stable release. Here is the syntax for
Rustup to install Rust from one of the channels.
$ rustup
$ rustup install beta
$ rustup install nightly
After installation, you can use the rustup self uninstall command to
uninstall the Rust environment.
Now that Rust is installed, it’s time for our “Hello, World” application.
“Hello, World”
Listing 2.1 shows the “Hello, World” application. Without a transfer control
statement, it runs sequentially, displays the text “Hello, world!”, and then
exits.
Snake case is the naming convention for functions in Rust. For this
convention, each word of the function name starts with a lowercase letter
and individual words are separated with underscores.
The main function is the entry point function for a Rust executable crate
and the starting point of the application. In Rust, our main function has no
function parameters or explicit return value.
Code for a function is encapsulated within curly braces, {}, which
demarcates a function block. For the main function, the program concludes
at the end of the main function block. It is the primary thread for the
application.
In the main function, the println! macro displays the “Hello, world!”
message and a linefeed. Function blocks can contain expressions,
statements, and the macro. Macros in Rust have an exclamation point (!)
after the name. In most instances, expressions and statements are terminated
with semicolons.
For more information about this error, try `rustc --explain E0423`
Cargo
You can use the cargo tool to compile Rust crates and create binaries. This
is in lieu of using the rustc compiler directly. cargo is a versatile tool that
can perform various tasks, including creating and managing packages,
compiling binaries, maintaining a secure environment, and managing
dependencies. For compilation, cargo delegates to the rustc compiler.
Because of the flexibility of this tool, Rustaceans often prefer cargo,
instead of rustc, for compilation. In addition, this means learning only one
command-line interface instead of two. Since most Rustaceans are probably
using cargo already for something, such as creating new packages, it is
simpler not to switch to a different tool. Keep in mind, though, that the
rustc tool will continue to be used, just indirectly.
The cargo new command, shown next, creates a new package for either
an executable or library crate. The default is an executable crate. A library
crate can be created with the --lib option.
cargo new name
The cargo new command also creates a directory structure for the new
package. Initially, this includes a root directory and src subdirectory. The
tool will add more directories as needed. In the root directory are two files:
.gitignore and cargo.toml. Either the main.rs or lib.rs crate is placed in the
src subdirectory, depending on whether this is an executable or library
package, respectively.
You can also create a package within an existing directory. From that
directory, issue the following command, and a package is created at that
location:
cargo init
The .gitignore file lists directories and files excluded from GitHub. This
initially includes the target subdirectory, which contains the compiled
binaries, and the cargo.lock file.
Cargo.toml is the manifest and configuration file for the package. The
TOML suffix is a reference to Tom’s Obvious Minimal Language and is a
standard format for a readable configuration file. Cargo.toml contains
important configuration details about the package, including the name of
the package. The cargo new command creates the initial cargo.toml, as
shown in Listing 2.2.
[dependencies]
You can compile the crate with the following command and create a
binary executable:
cargo build
This command must be executed from within the package. The cargo
build command performs an incremental build. Changes to the crate,
modifying the dependencies in cargo.toml, and other reasons may force a
full build instead of an incremental one.
The cargo build command creates a packageroot/target directory.
Within that directory, a debug or release directory is created, depending on
the type of the build target for the binary. The cargo build command
defaults to building a debug target. A debug binary has few, if any,
optimizations, which is ideal for debugging. The release binary is most
often optimized for either performance or size. The cargo build --release
command creates a release binary that is placed in the release directory.
You can run an executable crate with the following command:
cargo run
This must also be done from within the package. If the binary is not
already built, cargo build will be done first. For this reason, some skip the
separate build step entirely for executable crates and rely entirely on the
cargo run command.
Library
When you use cargo to create a library, the primary difference is the
creation of a lib.rs file, instead of main.rs and a “Hello, World” application.
The lib.rs file contains sample code within a function that performs a trivial
mathematical operation. All of this occurs within the context of a unit test.
The following command creates a new package with a library crate:
Click here to view code image
cargo new --lib packagename
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
Let’s explore the lib.rs crate. The file starts with the #[cfg(test)]
annotation. This annotation asks the cargo build command to ignore unit
tests and not to include them in the resulting binary. Within the file, each
unit test is labeled with the #[test] annotation. The sample unit test
performs a simple addition. The result of the operation is compared to the
expected value within the assert_eq! macro. You update the code in the
unit tests to reference the specific public functions of your library. There
should be a unit test for each public function in the library.
Listing 2.5 provides an example of a library crate with two functions.
The get_hello function is public and returns the “Hello, world!” string. The
test_get_hello function is a unit test and tests the get_hello function.
#[cfg(test)]
mod tests {
#[test]
fn test_get_hello() {
let result = get_hello();
assert_eq!(result, "Hello, world!");
}
}
You may want to create an executable crate that uses the library. The
cargo.toml file for the executable must be updated to reference the library.
Listing 2.6 shows the updated cargo.toml that references a local version of
the hello library in the dependency section. The change in the file is
highlighted in bold.
[dependencies]
hello={path = "../hello" }
With the preceding cargo.toml file, the executable crate can access the
public functions of the library. The syntax for accessing functions in the
library is libraryname::function. The code in Listing 2.7 calls the
get_hello function found in the hello library.
A raven was once sitting upon a tree with a nice bit of cheese in
his mouth. A fox near by, being hungry, approached the raven with
the design of getting the bit of cheese, if he could. So he began to
speak as follows:
“Good morning, Mr. Raven! How fine you look to-day! I never saw
your coat so rich and glossy before. Pray give me a bit of that
cheese; I am very fond of cheese.”
“Hem!” said the raven, taking care not to open his mouth, and
seeming to think that he was not such a ninny as to be flattered out
of his cheese by a fox. But reynard is a sort of natural lawyer, who
knows the weak points of people, and has a faculty, as well as a
disposition, to turn them to account. He thought to himself, “Now the
raven has a hoarse, croaking voice; and the way to flatter any one is
to praise that in which he is most deficient.” So he began:
“Well, my dear Raven, I told you I wanted the cheese—but, in
point of fact, I care nothing about it. I hate cheese, for it spoils the
breath; but I really wanted to hear you sing, and the cheese stops up
your mouth. I beg of you to sing me a little French or Italian air; you
execute those things so deliciously.”
The raven, like many other silly people who have odious voices,
fancied that he sang divinely; so he dropped the cheese, and began;
whereupon the fox picked up the cheese, and holding his bursting
sides, ran away, saying to himself, “O, flattery, flattery; it is the key
that unlocks all hearts. You have only to use the right kind, and you
can make a fool of anybody. But as to these people with croaking
throats, who pretend to sing French and Italian airs, bah! it is too
much!”
I don’t see why.
I know a little girl who has a very pleasant home, and the very
kindest of parents, and who is yet often discontented and unhappy.
She pouts her lips, and throws her arms about, and sulks, and
stamps with her feet, and makes a strange noise in her throat,
between a growl and a cry. It is not because she has not enough to
eat of good, wholesome food; nor because she has no time to play,
and playthings in abundance, and brothers to play with her. She is
not blind, nor lame, nor deformed in any way, but has health and
strength, and everything which any little girl could wish, to make her
happy in this world, but a good heart.
What was it, then, that made her fretful? Why, she had a kind
mother, who told her what she must do, and what she must not do. I
will tell you what I heard one day.
“Caroline, you must not take my scissors, my dear.”
“Why, mother? I have no scissors to cut off my thread,” said
Caroline, pettishly.
“Well, my dear, I will give you a pair, but you must not take mine.”
“I am sure I don’t see why; it’s only just to cut my thread.”
Now, these scissors were of the finest kind, and highly polished,
and Caroline’s mother knew that it would soil them if she should
handle them; and that if she had them once, she would want them
again. Caroline’s duty was to obey cheerfully, whether she saw the
reason why, or not.
“Caroline, my dear, you must not climb upon the chair to reach
your work. You must ask some one to get it for you.”
“I am sure I don’t see why. It is less trouble to get it myself than to
ask anybody for it.”
“Very well, my child, you shall do it in your own way, and see.”
That very afternoon, Caroline mounted on a chair to get her work.
She reached too far, and over went the chair, and Caroline with it.
Her work was scattered over the floor—the needlebook in one
direction, and the thimble in another, and the spools in another; and,
what was worse than all, her head struck the edge of the door, and a
gash was cut in her forehead. She cried sadly, and did not get over
the hurt for weeks. Was it less trouble to get it herself?
If she had trusted her mother, she would have saved herself all
this pain; but for the sake of knowing the reason why she could not
get upon the chair, she cost herself a severe wound, and a great
deal of shame and sorrow.
It is a good rule, through life, to do what God requires of us,
whether we see why or not. One of the things he requires of us to
do, is to obey our parents. (Eph. vi. 1. Col. iii. 20.)
CHAPTER VIII.
Character of the Indians.—Employed in the mines.—Story of a
pickaxe.—Mr. Temple’s conduct considered.—Humanity of the
Indians to him.—His reflections.—Dress of the Indian men;—
of the women.
Charles. Mother, may I play with the baby a little while before I go
to school?
Mother. She is asleep now, my son; but you may go softly and
look at her.
C. She is just going to wake up, mother! she is smiling and
moving her little hands.
M. No, she is only dreaming; don’t hold the curtain back so far,
the sun shines on her face.
C. I wonder what she is dreaming about; she looks very sober
now; what a pity she can’t tell us when she wakes! Mother, I shall be
glad when Susan grows a little bigger, and can run about, and talk,
and play with me; I don’t think a little baby is good for much.
M. And what if she should never grow up, Charles?
C. What! be always a little baby?
M. No, my son; what if she should die?
C. Die! O, that can’t be; she has only just begun to live.
M. Who made her live?
C. God, you told me.
M. And cannot God make her die when he pleases?
C. I suppose he can; but he never does, does he? Does he ever
kill such little babies as Susan?
M. They very often die, Charles.
C. I never heard of that before; I hope Susan will not die. How old
is she, mother?
M. Eight months.
C. O, mother, mother, that is too young to die; I am sure she
won’t. Here am I, seven years old, and I am not dead yet.
M. And I am twenty-seven, my dear boy; but for all that, you and
Susan may both die before I do, if it should please God.
C. What makes the tears come in your eyes, mother? we shan’t
die, I know. See how Susan keeps stirring about! see how red her
cheeks are!
M. She is not well; she is feverish, Charles. Do you know there
are two little white teeth trying to get through her gums, and they
give her a great deal of pain? I shall send for the doctor to-day. The
clock is striking nine, Charles, and you must go to school.
C. O dear! and where is my little satchel? and where is my
spelling-book, I wonder?
M. You had better look in the breakfast-room; and, Charles, be
sure you shut the window; it is very damp this morning.
C. Yes, mother. I wonder what I did with my cap.
M. Don’t bang the door, Charles—and don’t forget to shut the
window. I must take the baby down this morning.
tuesday morning.
Charles meets the doctor coming out of his mother’s chamber.
C. Are you the doctor, sir?
D. Yes, my little man.
C. Is the baby almost well again?
D. O no! no!
C. Why, they told me you were coming to cure her, and you came
three times yesterday; for I saw your old horse out of the school-
room window.
D. But she is very sick, little boy; somebody left a window open
yesterday when it was almost raining, and the nursery maid carried
her into a damp room while they were sweeping the nursery.
C. O, doctor, what shall I do? what shall I do?
D. Don’t cry, my little fellow; what is the matter, now?
C. It was I, it was I, that left the window open! mother told me to
shut it, and I was hunting for my cap and forgot all about it.
D. Well, that was wrong; but hush up; if your mother hears you
sobbing so bitterly she will feel much worse. It was a pity you forgot
the window.
C. O, my poor little sister! will you cure her? you can cure her sir,
can’t you sir?
D. I will try, but God must help us.
C. And won’t he help you? do you think he will make Susan die?
D. I cannot tell, indeed; but you must ask him to make her well.
C. How can I ask him?
D. In your prayers; do you not say your prayers every night?
C. Yes, the Lord’s prayer, and two other prayers; but there is
nothing in them about Susan’s being sick.
D. And can’t you make a little prayer on purpose?
C. I don’t know; I never tried.
D. Then go up into your chamber, my dear child, and kneel down
where you always say your prayers every night, and pray to God just
as if you could see him in the room with you. You may depend upon
it. He is there.
C. Shall I ask him to help you cure Susan?
D. Ask him to cure her if it is best she should get well.
C. Why, it is best certainly. And will it be wrong to tell him how
sorry I am that I forgot the window, and ask him to forgive me?
D. No, it will be quite right.
C. Then I will go this minute. You must come again before dinner
—won’t you?
D. Yes, I must indeed.
wednesday morning.
Charles comes softly into his mother’s chamber, half dressed.
C. Mother, are you there? it is so dark I cannot see you.
M. I am here, sitting by the bed, my son.
C. The fire is out, and the candle is just going out; may I open the
shutter a little way, so that I can see the baby, mother? I won’t wake
her.
M. She is not asleep, my dear boy. But what made you wake at
day-break?
C. I kept thinking of Susan when I was asleep, mother. What
makes her so still? is the pain better?
M. It is all gone, Charles; she will never feel it again; open the
shutters wide and come here.
C. O, mother, mother! (burying his face in her lap,) I do not wish to
look at her.
M. What is the matter, Charles? tell me.
C. She is dead—she is dead! the tears keep rolling down your
cheeks—and she is lying just like my little canary bird—and I do
believe she is dead!
M. Yes! my baby is dead, Charles! and—
C. Don’t cry, don’t cry! dear mother; you did not cry when I came
in—I will leave off crying if you will, mother.
M. Look at her little pale face, Charles;—why are you unwilling to
look at her?
C. I do not know. Will you take her off the bed? are you afraid to
hold her in your arms?
M. O, no; I have held her a great while to-night, Charles, and she
died in my lap.
C. And were you all alone?
M. No, there were two or three people with me then, and they
were very kind; but I sent them all away at last.
C. Why, mother?
M. Because sometimes I wanted to cry, and sometimes to pray,
and I liked better to be alone. I was praying when you came in,
Charles.
C. Mother, I prayed yesterday about Susan, but God did not mind
it. What makes you pray now that she is dead?
M. I was praying that I might remember how happy little Susan’s
soul is, and that I might not be so wicked as to complain because
God had taken her away again; and that I might be a better woman
now, and think more of heaven.
C. You need not pray for that, mother; you are a very good
woman, the best woman in the world.
M. Nobody can be good without praying, my son; and I had a
great many things to beg of God. I was asking him to make the little
boy who is spared to me, a good child.
C. Ah, mother, that is because I forgot the window!
M. No, my child, I was not thinking of that then; but if you should
pray to God to help you to cure your faults, you will find it becomes
much easier for you.
C. Then why did he not cure Susan’s sickness when I begged him
so hard?
M. Are you sure it would have been better for Susan to live?
C. I don’t know; she would have cried sometimes, I suppose.
M. But she never will cry now, Charles; her soul is with God in
heaven, and her body cannot feel pain now.
C. But it would have been better for us if she had lived to grow up,
mother. What makes you cry again?
Enter Aunt Catherine.
C. I am glad you have come, aunt; I have made mother cry again,
and I cannot help crying too. I do think it would have been better for
us if Susan had not died.
A. Your mother thought so at first, Charles; but now she knows it
would have been wrong to have wished little Susan here just for her
own pleasure, when the little creature is happier in heaven. Besides,
God would not have taken her if it had been for your mother’s real
good to let her stay.
C. I cannot understand that, do you mother?
M. I do! I do! but I cannot talk about it now.
C. So sudden! three days ago she was well!
A. Come, my dear child, come and let me finish dressing you, and
your mother will talk to you about Susan very often; kiss the dear
baby’s cheek, Charles,—your mother is holding her up to you.
C. O, if she could only be made alive again!
A. Hush—do not sob so loud! come with me, Charles, and I will
tell you how we think God has already made her alive in heaven.
John Doree.