返回首页返回书籍《The Rust Programming Language》
目录

目录 · CONTENTS

  1. 01The Rust Programming Language
  2. 02Foreword
  3. 03Introduction
  4. 04Getting Started
  5. 05Installation
  6. 06Hello, World!
  7. 07Hello, Cargo!
  8. 08Programming a Guessing Game
  9. 09Common Programming Concepts
  10. 10Variables and Mutability
  11. 11Data Types
  12. 12Functions
  13. 13Comments
  14. 14Control Flow
  15. 15Understanding Ownership
  16. 16What is Ownership?
  17. 17References and Borrowing
  18. 18The Slice Type
  19. 19Using Structs to Structure Related Data
  20. 20Defining and Instantiating Structs
  21. 21An Example Program Using Structs
  22. 22Methods
  23. 23Enums and Pattern Matching
  24. 24Defining an Enum
  25. 25The `match` Control Flow Construct
  26. 26Concise Control Flow with `if let` and `let...else`
  27. 27Packages, Crates, and Modules
  28. 28Packages and Crates
  29. 29Control Scope and Privacy with Modules
  30. 30Paths for Referring to an Item in the Module Tree
  31. 31Bringing Paths Into Scope with the `use` Keyword
  32. 32Separating Modules into Different Files
  33. 33Common Collections
  34. 34Storing Lists of Values with Vectors
  35. 35Storing UTF-8 Encoded Text with Strings
  36. 36Storing Keys with Associated Values in Hash Maps
  37. 37Error Handling
  38. 38Unrecoverable Errors with `panic!`
  39. 39Recoverable Errors with `Result`
  40. 40To `panic!` or Not to `panic!`
  41. 41Generic Types, Traits, and Lifetimes
  42. 42Generic Data Types
  43. 43Defining Shared Behavior with Traits
  44. 44Validating References with Lifetimes
  45. 45Writing Automated Tests
  46. 46How to Write Tests
  47. 47Controlling How Tests Are Run
  48. 48Test Organization
  49. 49An I/O Project: Building a Command Line Program
  50. 50Accepting Command Line Arguments
  51. 51Reading a File
  52. 52Refactoring to Improve Modularity and Error Handling
  53. 53Adding Functionality with Test Driven Development
  54. 54Working with Environment Variables
  55. 55Redirecting Errors to Standard Error
  56. 56Functional Language Features: Iterators and Closures
  57. 57Closures
  58. 58Processing a Series of Items with Iterators
  59. 59Improving Our I/O Project
  60. 60Performance in Loops vs. Iterators
  61. 61More About Cargo and Crates.io
  62. 62Customizing Builds with Release Profiles
  63. 63Publishing a Crate to Crates.io
  64. 64Cargo Workspaces
  65. 65Installing Binaries with `cargo install`
  66. 66Extending Cargo with Custom Commands
  67. 67Smart Pointers
  68. 68Using `Box<T>` to Point to Data on the Heap
  69. 69Treating Smart Pointers Like Regular References
  70. 70Running Code on Cleanup with the `Drop` Trait
  71. 71`Rc<T>`, the Reference Counted Smart Pointer
  72. 72`RefCell<T>` and the Interior Mutability Pattern
  73. 73Reference Cycles Can Leak Memory
  74. 74Fearless Concurrency
  75. 75Using Threads to Run Code Simultaneously
  76. 76Transfer Data Between Threads with Message Passing
  77. 77Shared-State Concurrency
  78. 78Extensible Concurrency with `Send` and `Sync`
  79. 79Fundamentals of Asynchronous Programming: Async, Await, Futures, and Streams
  80. 80extern crate trpl; // required for mdbook test
  81. 81Applying Concurrency with Async
  82. 82Working With Any Number of Futures
  83. 83Streams: Futures in Sequence
  84. 84A Closer Look at the Traits for Async
  85. 85Futures, Tasks, and Threads
  86. 86Object-Oriented Programming Features
  87. 87Characteristics of Object-Oriented Languages
  88. 88Using Trait Objects to Abstract over Shared Behavior
  89. 89Implementing an Object-Oriented Design Pattern
  90. 90Patterns and Matching
  91. 91All the Places Patterns Can Be Used
  92. 92Refutability: Whether a Pattern Might Fail to Match
  93. 93Pattern Syntax
  94. 94Advanced Features
  95. 95Unsafe Rust
  96. 96Advanced Traits
  97. 97Advanced Types
  98. 98Advanced Functions and Closures
  99. 99Macros
  100. 100Final Project: Building a Multithreaded Web Server
  101. 101Building a Single-Threaded Web Server
  102. 102From Single-Threaded to Multithreaded Server
  103. 103Graceful Shutdown and Cleanup
  104. 104Appendix
  105. 105A - Keywords
  106. 106B - Operators and Symbols
  107. 107C - Derivable Traits
  108. 108D - Useful Development Tools
  109. 109E - Editions
  110. 110F - Translations of the Book
  111. 111G - How Rust is Made and “Nightly Rust”
第 7 / 111 节

第 7 节 · SECTION 07

Hello, Cargo!

Hello, Cargo!

Cargo is Rust’s build system and package manager. Most Rustaceans use this tool to manage their Rust projects because Cargo handles a lot of tasks for you, such as building your code, downloading the libraries your code depends on, and building those libraries. (We call the libraries that your code needs dependencies.)

The simplest Rust programs, like the one we’ve written so far, don’t have any dependencies. If we had built the “Hello, world!” project with Cargo, it would only use the part of Cargo that handles building your code. As you write more complex Rust programs, you’ll add dependencies, and if you start a project using Cargo, adding dependencies will be much easier to do.

Because the vast majority of Rust projects use Cargo, the rest of this book assumes that you’re using Cargo too. Cargo comes installed with Rust if you used the official installers discussed in the “Installation” section. If you installed Rust through some other means, check whether Cargo is installed by entering the following in your terminal:

$ cargo --version

If you see a version number, you have it! If you see an error, such as command not found, look at the documentation for your method of installation to determine how to install Cargo separately.

Creating a Project with Cargo

Let’s create a new project using Cargo and look at how it differs from our original “Hello, world!” project. Navigate back to your projects directory (or wherever you decided to store your code). Then, on any operating system, run the following:

$ cargo new hello_cargo
$ cd hello_cargo

The first command creates a new directory and project called hello_cargo. We’ve named our project hello_cargo, and Cargo creates its files in a directory of the same name.

Go into the hello_cargo directory and list the files. You’ll see that Cargo has generated two files and one directory for us: a Cargo.toml file and a src directory with a main.rs file inside.

It has also initialized a new Git repository along with a .gitignore file. Git files won’t be generated if you run cargo new within an existing Git repository; you can override this behavior by using cargo new --vcs=git.

Note: Git is a common version control system. You can change cargo new to use a different version control system or no version control system by using the --vcs flag. Run cargo new --help to see the available options.

Open Cargo.toml in your text editor of choice. It should look similar to the code in Listing 1-2.

Listing 1-2: Contents of Cargo.toml generated by cargo new Filename: Cargo.toml

[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2024"

[dependencies]

This file is in the TOML (Tom’s Obvious, Minimal Language) format, which is Cargo’s configuration format.

The first line, [package], is a section heading that indicates that the following statements are configuring a package. As we add more information to this file, we’ll add other sections.

The next three lines set the configuration information Cargo needs to compile your program: the name, the version, and the edition of Rust to use. We’ll talk about the edition key in Appendix E.

The last line, [dependencies], is the start of a section for you to list any of your project’s dependencies. In Rust, packages of code are referred to as crates. We won’t need any other crates for this project, but we will in the first project in Chapter 2, so we’ll use this dependencies section then.

Now open src/main.rs and take a look:

Filename: src/main.rs

fn main() {
    println!("Hello, world!");
}

Cargo has generated a “Hello, world!” program for you, just like the one we wrote in Listing 1-1! So far, the differences between our project and the project Cargo generated are that Cargo placed the code in the src directory, and we have a Cargo.toml configuration file in the top directory.

Cargo expects your source files to live inside the src directory. The top-level project directory is just for README files, license information, configuration files, and anything else not related to your code. Using Cargo helps you organize your projects. There’s a place for everything, and everything is in its place.

If you started a project that doesn’t use Cargo, as we did with the “Hello, world!” project, you can convert it to a project that does use Cargo. Move the project code into the src directory and create an appropriate Cargo.toml file. One easy way to get that Cargo.toml file is to run cargo init, which will create it for you automatically.

Building and Running a Cargo Project

Now let’s look at what’s different when we build and run the “Hello, world!” program with Cargo! From your hello_cargo directory, build your project by entering the following command:

$ cargo build
   Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs

This command creates an executable file in target/debug/hello_cargo (or target\debug\hello_cargo.exe on Windows) rather than in your current directory. Because the default build is a debug build, Cargo puts the binary in a directory named debug. You can run the executable with this command:

$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows
Hello, world!

If all goes well, Hello, world! should print to the terminal. Running cargo build for the first time also causes Cargo to create a new file at the top level: Cargo.lock. This file keeps track of the exact versions of dependencies in your project. This project doesn’t have dependencies, so the file is a bit sparse. You won’t ever need to change this file manually; Cargo manages its contents for you.

We just built a project with cargo build and ran it with ./target/debug/hello_cargo, but we can also use cargo run to compile the code and then run the resultant executable all in one command:

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/hello_cargo`
Hello, world!

Using cargo run is more convenient than having to remember to run cargo build and then use the whole path to the binary, so most developers use cargo run.

Notice that this time we didn’t see output indicating that Cargo was compiling hello_cargo. Cargo figured out that the files hadn’t changed, so it didn’t rebuild but just ran the binary. If you had modified your source code, Cargo would have rebuilt the project before running it, and you would have seen this output:

$ cargo run
   Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
     Running `target/debug/hello_cargo`
Hello, world!

Cargo also provides a command called cargo check. This command quickly checks your code to make sure it compiles but doesn’t produce an executable:

$ cargo check
   Checking hello_cargo v0.1.0 (file:///projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs

Why would you not want an executable? Often, cargo check is much faster than cargo build because it skips the step of producing an executable. If you’re continually checking your work while writing the code, using cargo check will speed up the process of letting you know if your project is still compiling! As such, many Rustaceans run cargo check periodically as they write their program to make sure it compiles. Then, they run cargo build when they’re ready to use the executable.

Let’s recap what we’ve learned so far about Cargo:

  • We can create a project using cargo new.
  • We can build a project using cargo build.
  • We can build and run a project in one step using cargo run.
  • We can build a project without producing a binary to check for errors using cargo check.
  • Instead of saving the result of the build in the same directory as our code, Cargo stores it in the target/debug directory.

An additional advantage of using Cargo is that the commands are the same no matter which operating system you’re working on. So, at this point, we’ll no longer provide specific instructions for Linux and macOS versus Windows.

Building for Release

When your project is finally ready for release, you can use cargo build --release to compile it with optimizations. This command will create an executable in target/release instead of target/debug. The optimizations make your Rust code run faster, but turning them on lengthens the time it takes for your program to compile. This is why there are two different profiles: one for development, when you want to rebuild quickly and often, and another for building the final program you’ll give to a user that won’t be rebuilt repeatedly and that will run as fast as possible. If you’re benchmarking your code’s running time, be sure to run cargo build --release and benchmark with the executable in target/release.

Leveraging Cargo’s Conventions

With simple projects, Cargo doesn’t provide a lot of value over just using rustc, but it will prove its worth as your programs become more intricate. Once programs grow to multiple files or need a dependency, it’s much easier to let Cargo coordinate the build.

Even though the hello_cargo project is simple, it now uses much of the real tooling you’ll use in the rest of your Rust career. In fact, to work on any existing projects, you can use the following commands to check out the code using Git, change to that project’s directory, and build:

$ git clone example.org/someproject
$ cd someproject
$ cargo build

For more information about Cargo, check out its documentation.

Summary

You’re already off to a great start on your Rust journey! In this chapter, you learned how to:

  • Install the latest stable version of Rust using rustup.
  • Update to a newer Rust version.
  • Open locally installed documentation.
  • Write and run a “Hello, world!” program using rustc directly.
  • Create and run a new project using the conventions of Cargo.

This is a great time to build a more substantial program to get used to reading and writing Rust code. So, in Chapter 2, we’ll build a guessing game program. If you would rather start by learning how common programming concepts work in Rust, see Chapter 3 and then return to Chapter 2.