From ff93f82ff63ade5a352d9ccc430945d4ec804cdf Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 10 Jan 2018 15:57:36 -0500 Subject: [PATCH] Propagate doc changes to md --- second-edition/nostarch/chapter14.md | 658 +++++++++--------- .../src/ch14-00-more-about-cargo.md | 19 +- .../src/ch14-01-release-profiles.md | 65 +- .../src/ch14-02-publishing-to-crates-io.md | 348 ++++----- .../src/ch14-03-cargo-workspaces.md | 175 +++-- .../src/ch14-04-installing-binaries.md | 32 +- second-edition/src/ch14-05-extending-cargo.md | 21 +- 7 files changed, 663 insertions(+), 655 deletions(-) diff --git a/second-edition/nostarch/chapter14.md b/second-edition/nostarch/chapter14.md index a0c986b97..112ad80d3 100644 --- a/second-edition/nostarch/chapter14.md +++ b/second-edition/nostarch/chapter14.md @@ -1,36 +1,36 @@ [TOC] -# More about Cargo and Crates.io +# More About Cargo and Crates.io So far we’ve used only the most basic features of Cargo to build, run, and test -our code, but it can do a lot more. Here we’ll go over some of its other, more -advanced features to show you how to: +our code, but it can do a lot more. In this chapter, we’ll discuss some of its +other, more advanced features to show you how to: * Customize your build through release profiles -* Publish libraries on crates.io -* Organize larger projects with workspaces -* Install binaries from crates.io -* Extend Cargo with your own custom commands +* Publish libraries on *https://crates.io/* +* Organize large projects with workspaces +* Install binaries from *https://crates.io/* +* Extend Cargo using custom commands -Cargo can do even more than what we can cover in this chapter too, so for a -full explanation, see its documentation at *https://doc.rust-lang.org/cargo/*. +Cargo can do even more than what we cover in this chapter, so for a full +explanation of all its features, see its documentation at +*https://doc.rust-lang.org/cargo/*. ## Customizing Builds with Release Profiles -In Rust *release profiles* are pre-defined, and customizable, profiles with -different configurations, to allow the programmer more control over various -options for compiling your code. Each profile is configured independently of +In Rust, *release profiles* are predefined and customizable profiles with +different configurations that allow a programmer to have more control over +various options for compiling code. Each profile is configured independently of the others. -Cargo has two main profiles you should know about: the `dev` profile Cargo uses -when you run `cargo build`, and the `release` profile Cargo uses when you run -`cargo build --release`. The `dev` profile is defined with good defaults for -developing, and likewise the `release` profile has good defaults for release -builds. +Cargo has two main profiles: the `dev` profile Cargo uses when you run `cargo +build` and the `release` profile Cargo uses when you run `cargo build +--release`. The `dev` profile is defined with good defaults for developing, and +the `release` profile has good defaults for release builds. -These names may be familiar from the output of your builds, which shows the -profile used in the build: +These profile names might be familiar from the output of your builds, which +shows the profile used in the build: ``` $ cargo build @@ -39,16 +39,14 @@ $ cargo build --release Finished release [optimized] target(s) in 0.0 secs ``` -The “dev” and “release” notifications here indicate that the compiler is using -different profiles. - -### Customizing Release Profiles +The `dev` and `release` shown in this build output indicate that the compiler +is using different profiles. Cargo has default settings for each of the profiles that apply when there aren’t any `[profile.*]` sections in the project’s *Cargo.toml* file. By adding -`[profile.*]` sections for any profile we want to customize, we can choose to -override any subset of the default settings. For example, here are the default -values for the `opt-level` setting for the `dev` and `release` profiles: +`[profile.*]` sections for any profile we want to customize, we can override +any subset of the default settings. For example, here are the default values +for the `opt-level` setting for the `dev` and `release` profiles: Filename: Cargo.toml @@ -60,20 +58,20 @@ opt-level = 0 opt-level = 3 ``` -The `opt-level` setting controls how many optimizations Rust will apply to your -code, with a range of zero to three. Applying more optimizations makes -compilation take longer, so if you’re in development and compiling very often, -you’d want compiling to be fast at the expense of the resulting code running -slower. That’s why the default `opt-level` for `dev` is `0`. When you’re ready -to release, it’s better to spend more time compiling. You’ll only be compiling -in release mode once, and running the compiled program many times, so release -mode trades longer compile time for code that runs faster. That’s why the -default `opt-level` for the `release` profile is `3`. +The `opt-level` setting controls the number of optimizations Rust will apply to +your code with a range of zero to three. Applying more optimizations extends +compiling time, so if you’re in development and compiling your code often, you +want faster compiling even at the expense of the resulting code running slower. +That is the reason the default `opt-level` for `dev` is `0`. When you’re ready +to release your code, it’s best to spend more time compiling. You’ll only +compile in release mode once and run the compiled program many times, so +release mode trades longer compile time for code that runs faster. That is the +reason the default `opt-level` for the `release` profile is `3`. -We can choose to override any default setting by adding a different value for -them in *Cargo.toml*. If we wanted to use optimization level 1 in the -development profile, for example, we can add these two lines to our project’s -*Cargo.toml*: +We can override any default setting by adding a different value for it in +*Cargo.toml*. For example, if we want to use optimization level 1 in the +development profile, we can add these two lines to our project’s *Cargo.toml* +file: Filename: Cargo.toml @@ -82,40 +80,40 @@ Filename: Cargo.toml opt-level = 1 ``` -This overrides the default setting of `0`. Now when we run `cargo build`, Cargo -will use the defaults for the `dev` profile plus our customization to -`opt-level`. Because we set `opt-level` to `1`, Cargo will apply more -optimizations than the default, but not as many as a release build. +This code overrides the default setting of `0`. Now when we run `cargo` +`build`, Cargo will use the defaults for the `dev` profile plus our +customization to `opt-level`. Because we set `opt-level` to `1`, Cargo will +apply more optimizations than the default, but not as many as a release build. For the full list of configuration options and defaults for each profile, see Cargo’s documentation at *https://doc.rust-lang.org/cargo/*. ## Publishing a Crate to Crates.io -We’ve used packages from crates.io as dependencies of our project, but you can -also share your code for other people to use by publishing your own packages. -Crates.io distributes the source code of your packages, so it primarily hosts -code that’s open source. +We’ve used packages from *https://crates.io/* as dependencies of our project, +but you can also share your code for other people to use by publishing your own +packages. The crate registry at *https://crates.io/* distributes the source +code of your packages, so it primarily hosts code that is open source. Rust and Cargo have features that help make your published package easier for -people to find and use. We’ll talk about some of those features, then cover how -to publish a package. +people to use and to find in the first place. We’ll talk about some of these +features next, and then explain how to publish a package. ### Making Useful Documentation Comments Accurately documenting your packages will help other users know how and when to -use them, so it’s worth spending some time to write documentation. In Chapter -3, we discussed how to comment Rust code with `//`. Rust also has particular -kind of comment for documentation, known conveniently as *documentation +use them, so it’s worth spending time writing documentation. In Chapter 3, we +discussed how to comment Rust code using `//`. Rust also has a particular kind +of comment for documentation, which is known conveniently as *documentation comments*, that will generate HTML documentation. The HTML displays the -contents of documentation comments for public API items, intended for -programmers interested in knowing how to *use* your crate, as opposed to how +contents of documentation comments for public API items intended for +programmers interested in knowing how to *use* your crate as opposed to how your crate is *implemented*. Documentation comments use `///` instead of `//` and support Markdown notation -for formatting the text if you’d like. You place documentation comments just -before the item they are documenting. Listing 14-1 shows documentation comments -for an `add_one` function in a crate named `my_crate`: +for formatting the text if you want to use it. You place documentation comments +just before the item they’re documenting. Listing 14-1 shows documentation +comments for an `add_one` function in a crate named `my_crate`: Filename: src/lib.rs @@ -136,18 +134,18 @@ pub fn add_one(x: i32) -> i32 { Listing 14-1: A documentation comment for a function -Here, we give a description of what the `add_one` function does, then start a -section with the heading “Examples”, and code that demonstrates how to use the -`add_one` function. We can generate the HTML documentation from this -documentation comment by running `cargo doc`. This command runs the `rustdoc` -tool distributed with Rust and puts the generated HTML documentation in the -*target/doc* directory. +Here, we give a description of what the `add_one` function does, start a +section with the heading `Examples`, and then provide code that demonstrates +how to use the `add_one` function. We can generate the HTML documentation from +this documentation comment by running `cargo doc`. This command runs the +`rustdoc` tool distributed with Rust and puts the generated HTML documentation +in the *target/doc* directory. For convenience, running `cargo doc --open` will build the HTML for your current crate’s documentation (as well as the documentation for all of your crate’s dependencies) and open the result in a web browser. Navigate to the -`add_one` function and you’ll see how the text in the documentation comments -gets rendered, shown here in Figure 14-1: +`add_one` function and you’ll see how the text in the documentation comments is +rendered, as shown in Figure 14-1: Rendered HTML documentation for the `add_one` function of `my_crate` @@ -155,35 +153,35 @@ Figure 14-1: HTML documentation for the `add_one` function #### Commonly Used Sections -We used the `# Examples` markdown heading in Listing 14-1 to create a section -in the HTML with the title “Examples”. Some other sections that crate authors +We used the `# Examples` Markdown heading in Listing 14-1 to create a section +in the HTML with the title “Examples.” Some other sections that crate authors commonly use in their documentation include: -* **Panics**: The scenarios in which this function could `panic!`. Callers of - this function who don’t want their programs to panic should make sure that - they don’t call this function in these situations. -* **Errors**: If this function returns a `Result`, describing the kinds of +* **Panics**: The scenarios in which the function being documented could + `panic!`. Callers of the function who don’t want their programs to panic + should make sure they don’t call the function in these situations. +* **Errors**: If the function returns a `Result`, describing the kinds of errors that might occur and what conditions might cause those errors to be - returned can be helpful to callers so that they can write code to handle the + returned can be helpful to callers so they can write code to handle the different kinds of errors in different ways. -* **Safety**: If this function is `unsafe` to call (we will discuss unsafety in +* **Safety**: If the function is `unsafe` to call (we discuss unsafety in Chapter 19), there should be a section explaining why the function is unsafe - and covering the invariants that this function expects callers to uphold. + and covering the invariants that the function expects callers to uphold. -Most documentation comment sections don’t need all of these sections, but this -is a good list to check to remind you of the kinds of things that people +Most documentation comment sections don’t need all of these sections, but it’s +a good list to check to remind you of the aspects of your code that people calling your code will be interested in knowing about. #### Documentation Comments as Tests -Adding examples in code blocks in your documentation comments is a way to -clearly demonstrate how to use your library, but it has an additional bonus: -running `cargo test` will run the code examples in your documentation as tests! -Nothing is better than documentation with examples. Nothing is worse than -examples that don’t actually work because the code has changed since the -documentation has been written. Try running `cargo test` with the documentation -for the `add_one` function like in Listing 14-1; you should see a section in -the test results like this: +Adding examples in code blocks in your documentation comments can clearly +demonstrate how to use your library, and doing so has an additional bonus: +running `cargo test` will run the code examples in your documentation as +tests! Nothing is better than documentation with examples. But nothing is worse +than examples that don’t work because the code has changed since the +documentation was written. Run `cargo test` with the documentation for the +`add_one` function from Listing 14-1; you should see a section in the test +results like this: ``` Doc-tests my_crate @@ -191,25 +189,25 @@ the test results like this: running 1 test test src/lib.rs - add_one (line 5) ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` -Now try changing either the function or the example so that the `assert_eq!` in -the example will panic. Run `cargo test` again, and you’ll see that the doc -tests catch that the example and the code are out of sync from one another! +Now change either the function or the example so the `assert_eq!` in the +example panics. Run `cargo test` again; you’ll see that the doc tests catch +that the example and the code are out of sync from one another! #### Commenting Contained Items -There’s another style of doc comment, `//!`, that adds documentation to the -item that contains the comments, rather than adding documentation to the items -following the comments. These are typically used inside the crate root file +Another style of doc comment, `//!`, adds documentation to the item that +contains the comments rather than adding documentation to the items following +the comments. We typically use these doc comments inside the crate root file (*src/lib.rs* by convention) or inside a module to document the crate or the module as a whole. -For example, if we wanted to add documentation that described the purpose of -the `my_crate` crate that contains the `add_one` function, we can add -documentation comments that start with `//!` to the beginning of *src/lib.rs* -as shown in Listing 14-2: +For example, if we want to add documentation that describes the purpose of the +`my_crate` crate that contains the `add_one` function, we can add documentation +comments that start with `//!` to the beginning of the *src/lib.rs* file, as +shown in Listing 14-2: Filename: src/lib.rs @@ -231,7 +229,7 @@ that contains this comment rather than an item that follows this comment. In this case, the item that contains this comment is the *src/lib.rs* file, which is the crate root. These comments describe the entire crate. -If we run `cargo doc --open`, we’ll see these comments displayed on the front +When we run `cargo doc --open`, these comments will display on the front page of the documentation for `my_crate` above the list of public items in the crate, as shown in Figure 14-2: @@ -241,38 +239,38 @@ Figure 14-2: Rendered documentation for `my_crate` including the comment describing the crate as a whole Documentation comments within items are useful for describing crates and -modules especially. Use them to talk about the purpose of the container overall -to help users of your crate understand your organization. +modules especially. Use them to explain the purpose of the container overall to +help your crate users understand your organization. -#### Exporting a Convenient Public API with `pub use` +### Exporting a Convenient Public API with `pub use` -In Chapter 7, we covered how to organize our code into modules with the `mod` -keyword, how to make items public with the `pub` keyword, and how to bring -items into a scope with the `use` keyword. The structure that makes sense to -you while you’re developing a crate may not be very convenient for your users, -however. You may wish to organize your structs in a hierarchy containing -multiple levels, but people that want to use a type you’ve defined deep in the +In Chapter 7, we covered how to organize our code into modules using the `mod` +keyword, how to make items public using the `pub` keyword, and how to bring +items into a scope with the `use` keyword. However, the structure that makes +sense to you while you’re developing a crate might not be very convenient for +your users. You might want to organize your structs in a hierarchy containing +multiple levels, but people who want to use a type you’ve defined deep in the hierarchy might have trouble finding out that those types exist. They might -also be annoyed at having to type `use -my_crate::some_module::another_module::UsefulType;` rather than `use -my_crate::UsefulType;`. +also be annoyed at having to enter `use` +`my_crate::some_module::another_module::UsefulType;` rather than `use` +`my_crate::UsefulType;`. The structure of your public API is a major consideration when publishing a crate. People who use your crate are less familiar with the structure than you -are, and might have trouble finding the pieces they want to use if the module -hierarchy is large. +are and might have difficulty finding the pieces they want to use if your crate +has a large module hierarchy. -The good news is that, if the structure *isn’t* convenient for others to use +The good news is that if the structure *isn’t* convenient for others to use from another library, you don’t have to rearrange your internal organization: -you can choose to re-export items to make a public structure that’s different -to your private structure, using `pub use`. Re-exporting takes a public item in -one location and makes it public in another location as if it was defined in -the other location instead. +instead, you can re-export items to make a public structure that’s different +than your private structure by using `pub use`. Re-exporting takes a public +item in one location and makes it public in another location, as if it was +defined in the other location instead. For example, say we made a library named `art` for modeling artistic concepts. -Within this library is a `kinds` module containing two enums named -`PrimaryColor` and `SecondaryColor` and a `utils` module containing a function -named `mix` as shown in Listing 14-3: +Within this library are two modules: a `kinds` module containing two enums +named `PrimaryColor` and `SecondaryColor`, and a `utils` module containing a +function named `mix`, as shown in Listing 14-3: Filename: src/lib.rs @@ -311,8 +309,8 @@ pub mod utils { Listing 14-3: An `art` library with items organized into `kinds` and `utils` modules -The front page of the documentation for this crate generated by `cargo doc` -would look like Figure 14-3: +Figure 14-3 shows what the front page of the documentation for this crate +generated by `cargo doc` would look like: Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules @@ -320,13 +318,13 @@ Figure 14-3: Front page of the documentation for `art` that lists the `kinds` and `utils` modules Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on the -front page, nor is the `mix` function. We have to click on `kinds` and `utils` -in order to see them. +front page, nor is the `mix` function. We have to click `kinds` and `utils` to +see them. -Another crate depending on this library would need `use` statements that import -the items from `art` including specifying the module structure that’s currently -defined. Listing 14-4 shows an example of a crate that uses the `PrimaryColor` -and `mix` items from the `art` crate: +Another crate that depends on this library would need `use` statements that +import the items from `art`, including specifying the module structure that’s +currently defined. Listing 14-4 shows an example of a crate that uses the +`PrimaryColor` and `mix` items from the `art` crate: Filename: src/main.rs @@ -346,18 +344,19 @@ fn main() { Listing 14-4: A crate using the `art` crate’s items with its internal structure exported -The author of the code in Listing 14-4 that uses the `art` crate had to figure -out that `PrimaryColor` is in the `kinds` module and `mix` is in the `utils` -module. The module structure of the `art` crate is more relevant to developers -working on the `art` crate than developers using the `art` crate. The internal -structure that organizes parts of the crate into the `kinds` module and the -`utils` module doesn’t add any useful information to someone trying to -understand how to use the `art` crate. The `art` crate’s module structure adds -confusion in having to figure out where to look and inconvenience in having to +The author of the code in Listing 14-4, which uses the `art` crate, had to +figure out that `PrimaryColor` is in the `kinds` module and `mix` is in the +`utils` module. The module structure of the `art` crate is more relevant to +developers working on the `art` crate than developers using the `art` crate. +The internal structure that organizes parts of the crate into the `kinds` +module and the `utils` module doesn’t contain any useful information for +someone trying to understand how to use the `art` crate. Instead, the `art` +crate’s module structure causes confusion because developers have to figure out +where to look, and the structure is inconvenient because developers must specify the module names in the `use` statements. -To remove the internal organization from the public API, we can take the `art` -crate code from Listing 14-3 and add `pub use` statements to re-export the +To remove the internal organization from the public API, we can modify the +`art` crate code in Listing 14-3 to add `pub use` statements to re-export the items at the top level, as shown in Listing 14-5: Filename: src/lib.rs @@ -382,17 +381,18 @@ pub mod utils { Listing 14-5: Adding `pub use` statements to re-export items -The API documentation generated with `cargo doc` for this crate will now list -and link re-exports on the front page as shown in Figure 14-4, which makes -these types easier to find. +The API documentation that `cargo doc` generates for this crate will now list +and link re-exports on the front page, as shown in Figure 14-4, which makes the +`PrimaryColor` and `SecondaryColor` types and the `mix` function easier to find: Rendered documentation for the `art` crate with the re-exports on the front page -Figure 14-4: Front page of the documentation for `art` that lists the re-exports +Figure 14-4: The front page of the documentation for `art` that lists the +re-exports -Users of the `art` crate can still see and choose to use the internal structure -as in Listing 14-3, or they can use the more convenient structure from Listing -14-5, as shown in Listing 14-6: +The `art` crate users can still see and use the internal structure from Listing +14-3 as demonstrated in Listing 14-4, or they can use the more convenient +structure in Listing 14-5, as shown in Listing 14-6: Filename: src/main.rs @@ -410,48 +410,48 @@ fn main() { Listing 14-6: A program using the re-exported items from the `art` crate In cases where there are many nested modules, re-exporting the types at the top -level with `pub use` can make a big difference in the experience of people who -use the crate. +level with `pub use` can make a significant difference in the experience of +people who use the crate. Creating a useful public API structure is more of an art than a science, and -you can iterate to find the API that works best for your users. Choosing `pub -use` gives you flexibility in how you structure your crate internally, and -decouples that internal structure with what you present to your users. Take a -look at some of the code of crates you’ve installed to see if their internal -structure differs from their public API. +you can iterate to find the API that works best for your users. Choosing `pub` +`use` gives you flexibility in how you structure your crate internally and +decouples that internal structure with what you present to your users. Look at +some of the code of crates you’ve installed to see if their internal structure +differs from their public API. -### Setting up a Crates.io Account +### Setting Up a Crates.io Account -Before you can publish any crates, you need to create an account on crates.io -and get an API token. To do so, visit the home page at *https://crates.io* and -log in via a GitHub account—the GitHub account is a requirement for now, but -the site may support other ways of creating an account in the future. Once -you’re logged in, visit your account settings at *https://crates.io/me* and -retrieve your API key. Then run the `cargo login` command with your API key, -like this: +Before you can publish any crates, you need to create an account on +*https://crates.io/* and get an API token. To do so, visit the home page at +*https://crates.io/* and log in via a GitHub account: the GitHub account is +currently a requirement, but the site might support other ways of creating an +account in the future. Once you’re logged in, visit your account settings at +*https://crates.io/me/* and retrieve your API key. Then run the `cargo` +`login` command with your API key, like this: ``` $ cargo login abcdefghijklmnopqrstuvwxyz012345 ``` This command will inform Cargo of your API token and store it locally in -*~/.cargo/credentials*. Note that this token is a *secret* and should not be -shared with anyone else. If it is shared with anyone for any reason, you should -revoke it and generate a new token on Crates.io. +*~/.cargo/credentials*. Note that this token is a *secret*: do not share it +with anyone else. If you do share it with anyone for any reason, you should +revoke it and generate a new token on *https://crates.io/*. ### Before Publishing a New Crate -Now you have an account, and let’s say you already have a crate you want to -publish. Before publishing, you’ll need to add some metadata to your crate by -adding it to the `[package]` section of the crate’s *Cargo.toml*. +Now that you have an account, let’s say you have a crate you want to publish. +Before publishing, you’ll need to add some metadata to your crate by adding it +to the `[package]` section of the crate’s *Cargo.toml* file. -Your crate will first need a unique name. While you’re working on a crate -locally, you may name a crate whatever you’d like. However, crate names on -Crates.io are allocated on a first-come-first-serve basis. Once a crate name is -taken, no one else may publish a crate with that name. Search for the name -you’d like to use on the site to find out if it has been taken. If it hasn’t, -edit the name in *Cargo.toml* under `[package]` to have the name you want to -use for publishing like so: +Your crate will need a unique name. While you’re working on a crate locally, +you can name a crate whatever you’d like. However, crate names on +*https://crates.io/* are allocated on a first-come, first-served basis. Once +a crate name is taken, no one else can publish a crate with that name. Search +for the name you want to use on the site to find out if it has been used. If it +hasn’t, edit the name in the *Cargo.toml* file under `[package]` to use the +name for publishing, like so: Filename: Cargo.toml @@ -460,8 +460,8 @@ Filename: Cargo.toml name = "guessing_game" ``` -Even if you’ve chosen a unique name, if you try to run `cargo publish` to -publish the crate at this point, you’ll get a warning and then an error: +Even if you’ve chosen a unique name, when you run `cargo publish` to publish +the crate at this point, you’ll get a warning and then an error: ``` $ cargo publish @@ -472,17 +472,17 @@ homepage or repository. error: api errors: missing or empty metadata fields: description, license. ``` -This is because we’re missing some crucial information: a description and -license are required so that people will know what your crate does and under -what terms they may use it. To rectify this error, we need to include this -information in *Cargo.toml*. +The reason is that you’re missing some crucial information: a description and +license are required so people will know what your crate does and under what +terms they can use it. To rectify this error, you need to include this +information in the *Cargo.toml* file. -Make a description that’s just a sentence or two, as it will appear with your -crate in search results and on your crate’s page. For the `license` field, you -need to give a *license identifier value*. The Linux Foundation’s Software -Package Data Exchange (SPDX) at *http://spdx.org/licenses/* lists the -identifiers you can use for this value. For example, to specify that you’ve -licensed your crate using the MIT License, add the `MIT` identifier: +Add a description that is just a sentence or two, because it will appear with +your crate in search results. For the `license` field, you need to give a +*license identifier value*. The Linux Foundation’s Software Package Data +Exchange (SPDX) at *http://spdx.org/licenses/* lists the identifiers you can +use for this value. For example, to specify that you’ve licensed your crate +using the MIT License, add the `MIT` identifier: Filename: Cargo.toml @@ -493,19 +493,19 @@ license = "MIT" ``` If you want to use a license that doesn’t appear in the SPDX, you need to place -the text of that license in a file, include the file in your project, then use -`license-file` to specify the name of that file instead of using the `license` -key. +the text of that license in a file, include the file in your project, and then +use `license-file` to specify the name of that file instead of using the +`license` key. -Guidance on which license is right for your project is out of scope for this -book. Many people in the Rust community choose to license their projects in the -same way as Rust itself, with a dual license of `MIT/Apache-2.0`—this +Guidance on which license is appropriate for your project is beyond the scope +of this book. Many people in the Rust community license their projects in the +same way as Rust by using a dual license of `MIT OR Apache-2.0`, which demonstrates that you can also specify multiple license identifiers separated -by a slash. +by `OR` to have multiple licenses for your project. -So, with a unique name, the version, and author details that `cargo new` added -when you created the crate, your description, and the license you chose added, -the *Cargo.toml* for a project that’s ready to publish might look like this: +With a unique name, the version, the author details that `cargo new` added +when you created the crate, your description, and a license added, the +*Cargo.toml* file for a project that is ready to publish might look like this: Filename: Cargo.toml @@ -515,29 +515,31 @@ name = "guessing_game" version = "0.1.0" authors = ["Your Name "] description = "A fun game where you guess what number the computer has chosen." -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" [dependencies] ``` Cargo’s documentation at *https://doc.rust-lang.org/cargo/* describes other -metadata you can specify to ensure your crate can be discovered and used more +metadata you can specify to ensure others can discover and use your crate more easily! ### Publishing to Crates.io Now that you’ve created an account, saved your API token, chosen a name for your crate, and specified the required metadata, you’re ready to publish! -Publishing a crate uploads a specific version to crates.io for others to use. +Publishing a crate uploads a specific version to *https://crates.io/* for +others to use. -Take care when publishing a crate, because a publish is *permanent*. The +Be careful when publishing a crate because a publish is *permanent*. The version can never be overwritten, and the code cannot be deleted. One major -goal of Crates.io is to act as a permanent archive of code so that builds of -all projects that depend on crates from Crates.io will continue to work. -Allowing deletion of versions would make fulfilling that goal impossible. -However, there is no limit to the number of versions of a crate you can publish. +goal of *https://crates.io/* is to act as a permanent archive of code so that +builds of all projects that depend on crates from *https://crates.io/* will +continue to work. Allowing version deletions would make fulfilling that goal +impossible. However, there is no limit to the number of crate versions you can +publish. -Let’s run the `cargo publish` command again. It should succeed now: +Run the `cargo publish` command again. It should succeed now: ``` $ cargo publish @@ -556,22 +558,22 @@ anyone can easily add your crate as a dependency of their project. ### Publishing a New Version of an Existing Crate When you’ve made changes to your crate and are ready to release a new version, -you change the `version` value specified in your *Cargo.toml* and republish. -Use the Semantic Versioning rules at *http://semver.org/* to decide what an -appropriate next version number is based on the kinds of changes you’ve made. -Then run `cargo publish` to upload the new version. +you change the `version` value specified in your *Cargo.toml* file and +republish. Use the Semantic Versioning rules at *http://semver.org/* to +decide what an appropriate next version number is based on the kinds of changes +you’ve made. Then run `cargo publish` to upload the new version. ### Removing Versions from Crates.io with `cargo yank` -While you can’t remove previous versions of a crate, you can prevent any future -projects from adding them as a new dependency. This is useful when a version of -a crate ends up being broken for one reason or another. For situations such as -this, Cargo supports *yanking* a version of a crate. +Although you can’t remove previous versions of a crate, you can prevent any +future projects from adding them as a new dependency. This is useful when a +crate version is broken for one reason or another. In such situations, Cargo +supports *yanking* a crate version. Yanking a version prevents new projects from starting to depend on that version while allowing all existing projects that depend on it to continue to download and depend on that version. Essentially, a yank means that all projects with a -*Cargo.lock* will not break, while any future *Cargo.lock* files generated will +*Cargo.lock* will not break, and any future *Cargo.lock* files generated will not use the yanked version. To yank a version of a crate, run `cargo yank` and specify which version you @@ -581,33 +583,32 @@ want to yank: $ cargo yank --vers 1.0.1 ``` -You can also undo a yank, and allow projects to start depending on a version -again, by adding `--undo` to the command: +By adding `--undo` to the command, you can also undo a yank and allow projects +to start depending on a version again: ``` $ cargo yank --vers 1.0.1 --undo ``` -A yank *does not* delete any code. The yank feature is not intended for -deleting accidentally uploaded secrets, for example. If that happens, you must +A yank *does not* delete any code. For example, the yank feature is not +intended for deleting accidentally uploaded secrets. If that happens, you must reset those secrets immediately. ## Cargo Workspaces -In Chapter 12, we built a package that included both a binary crate and a -library crate. You may find, as your project develops, that the library crate -continues to get bigger and you want to split your package up further into -multiple library crates. In this situation, Cargo has a feature called +In Chapter 12, we built a package that included a binary crate and a library +crate. As your project develops, you might find that the library crate +continues to get bigger and you want to split up your package further into +multiple library crates. In this situation, Cargo offers a feature called *workspaces* that can help manage multiple related packages that are developed in tandem. -A *workspace* is a set of packages that will all share the same *Cargo.lock* -and output directory. Let’s make a project using a workspace, using trivial -code so we can concentrate on the structure of a workspace. We’ll have a binary -that uses two libraries: one library that will provide an `add_one` function -and a second library that will provide an `add_two` function. These three -crates will all be part of the same workspace. We’ll start by creating a new -crate for the binary: +A *workspace* is a set of packages that share the same *Cargo.lock* and output +directory. Let’s make a project using a workspace and use trivial code so we +can concentrate on the structure of the workspace. We’ll have a binary that +uses two libraries: one library that provides an `add_one` function and a +second library that provides an `add_two` function. These three crates will be +part of the same workspace. We’ll start by creating a new crate for the binary: ``` $ cargo new --bin adder @@ -615,9 +616,9 @@ $ cargo new --bin adder $ cd adder ``` -We need to modify the binary package’s *Cargo.toml* and add a `[workspace]` -section to tell Cargo the `adder` package is a workspace. Add this at the -bottom of the file: +We need to modify the binary package’s *Cargo.toml* file and add a +`[workspace]` section to tell Cargo the `adder` package is a workspace. Add the +following to the bottom of the file: Filename: Cargo.toml @@ -625,22 +626,22 @@ Filename: Cargo.toml [workspace] ``` -Like many Cargo features, workspaces support convention over configuration: we -don’t need to add anything more than this to *Cargo.toml* to define our -workspace as long as we follow the convention. +As with many Cargo features, workspaces support convention over configuration: +we don’t need to add anything more than this to the *Cargo.toml* file to define +our workspace as long as we follow the convention. ### Specifying Workspace Dependencies -By default, Cargo will include all transitive path dependencies. A *path -dependency* is when any crate, whether in a workspace or not, specifies that it -has a dependency on a crate in a local directory by using the `path` attribute -on the dependency specification in *Cargo.toml*. If a crate has the -`[workspace]` key, or if the crate is itself part of a workspace, and we -specify path dependencies where the paths are subdirectories of the crate’s -directory, those dependent crates will be considered part of the workspace. -Let’s specify in the *Cargo.toml* for the top-level `adder` crate that it will -have a dependency on an `add-one` crate that will be in the `add-one` -subdirectory, by changing *Cargo.toml* to look like this: +By default, Cargo includes all transitive path dependencies. A *path +dependency* is used when any crate, whether in a workspace or not, specifies +that it has a dependency on a crate in a local directory by using the `path` +attribute on the dependency specification in the *Cargo.toml* file. If a crate +has the `[workspace]` key or if the crate is part of a workspace and we specify +path dependencies where the paths are subdirectories of the crate’s directory, +those dependent crates will be considered part of the workspace. Let’s specify +in the *Cargo.toml* file for the top-level `adder` crate that it will have a +dependency on an `add-one` crate that will be in the `add-one` subdirectory by +changing the *Cargo.toml* file to look like this: Filename: Cargo.toml @@ -649,20 +650,20 @@ Filename: Cargo.toml add-one = { path = "add-one" } ``` -If we add dependencies to *Cargo.toml* that don’t have a `path` specified, -those dependencies will be normal dependencies that aren’t in this workspace -and are assumed to come from Crates.io. +If we add dependencies to the *Cargo.toml* file that don’t have a `path` +specified, those dependencies will be normal dependencies that aren’t in this +workspace and are assumed to come from *https://crates.io/*. ### Creating the Second Crate in the Workspace -Next, while in the `adder` directory, generate an `add-one` crate: +Next, while in the *adder* directory, generate an `add-one` crate: ``` $ cargo new add-one Created library `add-one` project ``` -Your `adder` directory should now have these directories and files: +Your *adder* directory should now have these directories and files: ``` ├── Cargo.toml @@ -674,7 +675,7 @@ Your `adder` directory should now have these directories and files: └── main.rs ``` -In *add-one/src/lib.rs*, let’s add an `add_one` function: +In the *add-one/src/lib.rs* file, let’s add an `add_one` function: Filename: add-one/src/lib.rs @@ -684,9 +685,9 @@ pub fn add_one(x: i32) -> i32 { } ``` -Open up *src/main.rs* for `adder` and add an `extern crate` line at the top of -the file to bring the new `add-one` library crate into scope. Then change the -`main` function to call the `add_one` function, as in Listing 14-7: +Open the *src/main.rs* file for `adder` and add an `extern crate` line at the +top to bring the new `add-one` library crate into scope. Then change the `main` +function to call the `add_one` function, as in Listing 14-7: Filename: src/main.rs @@ -701,7 +702,8 @@ fn main() { Listing 14-7: Using the `add-one` library crate from the `adder` crate -Let’s build the `adder` crate by running `cargo build` in the *adder* directory! +Let’s build the `adder` crate by running `cargo build` in the *adder* +directory! ``` $ cargo build @@ -710,7 +712,7 @@ $ cargo build Finished dev [unoptimized + debuginfo] target(s) in 0.68 secs ``` -Note that this builds both the `adder` crate and the `add-one` crate in +Note that this builds the `adder` crate and the `add-one` crate in *adder/add-one*. Now your *adder* directory should have these files: ``` @@ -725,29 +727,27 @@ Note that this builds both the `adder` crate and the `add-one` crate in └── target ``` -The workspace has one *target* directory at the top level; *add-one* doesn’t -have its own *target* directory. Even if we go into the `add-one` directory and -run `cargo build`, the compiled artifacts end up in *adder/target* rather than -*adder/add-one/target*. The crates in a workspace depend on each other. If each -crate had its own *target* directory, each crate in the workspace would have to -recompile each other crate in the workspace in order to have the artifacts in -its own *target* directory. By sharing one *target* directory, the crates in -the workspace can avoid rebuilding the other crates in the workspace more than -necessary. +The workspace has one *target* directory at the top level; the `add-one` crate +doesn’t have its own *target* directory. Even if we go into the *add-one* +directory and run `cargo build`, the compiled artifacts end up in +*adder/target* rather than *adder/add-one/target*. The crates in a workspace +depend on each other. If each crate had its own *target* directory, each crate +in the workspace would have to recompile each of the other crates in the +workspace to have the artifacts in its own *target* directory. By sharing one +*target* directory, the crates in the workspace can avoid rebuilding the other +crates in the workspace more than necessary. #### Depending on an External Crate in a Workspace -Also notice the workspace only has one *Cargo.lock*, rather than having a +Notice that the workspace has only one *Cargo.lock* rather than having a top-level *Cargo.lock* and *add-one/Cargo.lock*. This ensures that all crates are using the same version of all dependencies. If we add the `rand` crate to -both *Cargo.toml* and *add-one/Cargo.toml*, Cargo will resolve both of those to -one version of `rand` and record that in the one *Cargo.lock*. Making all -crates in the workspace use the same dependencies means the crates in the -workspace will always be compatible with each other. Let’s try this out now. - -Let’s add the `rand` crate to the `[dependencies]` section in -*add-one/Cargo.toml* in order to be able to use the `rand` crate in the -`add-one` crate: +the *Cargo.toml* and *add-one/Cargo.toml* files, Cargo will resolve both of +those to one version of `rand` and record that in the one *Cargo.lock*. Making +all crates in the workspace use the same dependencies means the crates in the +workspace will always be compatible with each other. Let’s add the `rand` crate +to the `[dependencies]` section in the *add-one/Cargo.toml* file to be able to +use the `rand` crate in the `add-one` crate: Filename: add-one/Cargo.toml @@ -757,9 +757,9 @@ Filename: add-one/Cargo.toml rand = "0.3.14" ``` -We can now add `extern crate rand;` to *add-one/src/lib.rs*, and building the -whole workspace by running `cargo build` in the *adder* directory will bring in -and compile the `rand` crate: +We can now add `extern crate rand;` to the *add-one/src/lib.rs* file, and +building the whole workspace by running `cargo build` in the *adder* +directory will bring in and compile the `rand` crate: ``` $ cargo build @@ -772,11 +772,12 @@ $ cargo build Finished dev [unoptimized + debuginfo] target(s) in 10.18 secs ``` -The top level *Cargo.lock* now contains information about `add-one`’s -dependency on `rand`. However, even though `rand` is used somewhere in the +The top-level *Cargo.lock* now contains information about the dependency of +`add-one` on `rand`. However, even though `rand` is used somewhere in the workspace, we can’t use it in other crates in the workspace unless we add -`rand` to their *Cargo.toml* as well. If we add `extern crate rand;` to -*src/main.rs* for the top level `adder` crate, for example, we’ll get an error: +`rand` to their *Cargo.toml* files as well. For example, if we add `extern` +`crate rand;` to the *src/main.rs* file for the top-level `adder` crate, +we’ll get an error: ``` $ cargo build @@ -788,13 +789,13 @@ error[E0463]: can't find crate for `rand` | ^^^^^^^^^^^^^^^^^^^ can't find crate ``` -To fix this, edit *Cargo.toml* for the top level `adder` crate and indicate -that `rand` is a dependency for that crate as well. Building the `adder` crate -will add `rand` to the list of dependencies for `adder` in *Cargo.lock*, but no -additional copies of `rand` will be downloaded. Cargo has ensured for us that -any crate in the workspace using the `rand` crate will be using the same -version. Using the same version of `rand` across the workspace saves space -since we won’t have multiple copies and ensures that the crates in the +To fix this, edit the *Cargo.toml* file for the top-level `adder` crate and +indicate that `rand` is a dependency for that crate as well. Building the +`adder` crate will add `rand` to the list of dependencies for `adder` in +*Cargo.lock*, but no additional copies of `rand` will be downloaded. Cargo has +ensured that any crate in the workspace using the `rand` crate will be using +the same version. Using the same version of `rand` across the workspace saves +space because we won’t have multiple copies and ensures that the crates in the workspace will be compatible with each other. #### Adding a Test to a Workspace @@ -833,9 +834,9 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured ``` -Wait a second, zero tests? We just added one! If we look at the output, we can -see that `cargo test` in a workspace only runs tests for the top level crate. -To run tests for all of the crates in the workspace, we need to pass the +Wait a second, zero tests? We just added one! When we look at the output, we +can see that `cargo test` in a workspace only runs tests for the top-level +crate. To run tests for all the crates in the workspace, we need to pass the `--all` flag: ``` @@ -861,10 +862,9 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` -When passing `--all`, `cargo test` will run the tests for all of the crates in -the workspace. We can also choose to run tests for one particular crate in a -workspace from the top level directory by using the `-p` flag and specifying -the name of the crate we want to test: +We can also run tests for one particular crate in a workspace from the +top-level directory by using the `-p` flag and specifying the name of the crate +we want to test: ``` $ cargo test -p add-one @@ -886,40 +886,41 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out This output shows `cargo test` only ran the tests for the `add-one` crate and didn’t run the `adder` crate tests. -If you choose to publish the crates in the workspace to crates.io, each crate -in the workspace will get published separately. The `cargo publish` command -does not have an `--all` flag or a `-p` flag, so it is necessary to change to -each crate’s directory and run `cargo publish` on each crate in the workspace -in order to publish them. +If you publish the crates in the workspace to *https://crates.io/*, each +crate in the workspace will need to be published separately. The `cargo` +`publish` command does not have an `--all` flag or a `-p` flag, so you must +change to each crate’s directory and run `cargo publish` on each crate in the +workspace to publish them. -Now try adding an `add-two` crate to this workspace in a similar way as the -`add-one` crate for some more practice! +For additional practice, add an `add-two` crate to this workspace in a similar +way as the `add-one` crate! -As your project grows, consider using a workspace: smaller components are -easier to understand individually than one big blob of code. Keeping the crates -in a workspace can make coordination among them easier if they work together -and are often changed at the same time. +As your project grows, consider using a workspace: it’s easier to understand +smaller, individual components than one big blob of code. Keeping the crates in +a workspace can make coordination between them easier if they are often changed +at the same time. ## Installing Binaries from Crates.io with `cargo install` The `cargo install` command allows you to install and use binary crates locally. This isn’t intended to replace system packages; it’s meant to be a convenient way for Rust developers to install tools that others have shared on -crates.io. Only packages that have binary targets can be installed. A binary -target is the runnable program that gets created if the crate has a -*src/main.rs* or another file specified as a binary, as opposed to a library -target that isn’t runnable on its own but is suitable for including within -other programs. Usually, crates have information in the *README* file about -whether a crate is a library, has a binary target, or both. +*https://crates.io/*. You can only install packages that have binary targets. +A binary target is the runnable program that is created if the crate has a +*src/main.rs* file or another file specified as a binary, as opposed to a +library target that isn’t runnable on its own but is suitable for including +within other programs. Usually, crates have information in the *README* file +about whether a crate is a library, has a binary target, or both. -All binaries from `cargo install` are put into the installation root’s *bin* -folder. If you installed Rust using *rustup.rs* and don’t have any custom -configurations, this will be `$HOME/.cargo/bin`. Ensure that directory is in -your `$PATH` to be able to run programs you’ve gotten through `cargo install`. +All binaries installed with `cargo install` are stored in the installation +root’s *bin* folder. If you installed Rust using *rustup.rs* and don’t have any +custom configurations, this directory will be *$HOME/.cargo/bin*. Ensure that +directory is in your `$PATH` to be able to run programs you’ve installed with +`cargo install`. -For example, we mentioned in Chapter 12 that there’s a Rust implementation of -the `grep` tool for searching files called `ripgrep`. If we want to install -`ripgrep`, we can run: +For example, in Chapter 12 we mentioned that there’s a Rust implementation of +the `grep` tool called `ripgrep` for searching files. If we want to install +`ripgrep`, we can run the following: ``` $ cargo install ripgrep @@ -933,22 +934,23 @@ Updating registry `https://github.com/rust-lang/crates.io-index` The last line of the output shows the location and the name of the installed binary, which in the case of `ripgrep` is `rg`. As long as the installation -directory is in your `$PATH` as mentioned above, you can then run `rg --help` -and start using a faster, rustier tool for searching files! +directory is in your `$PATH`, as mentioned previously, you can then run `rg` +`--help` and start using a faster, rustier tool for searching files! ## Extending Cargo with Custom Commands Cargo is designed so you can extend it with new subcommands without having to -modify Cargo itself. If a binary in your `$PATH` is named `cargo-something`, -you can run it as if it were a Cargo subcommand by running `cargo something`. -Custom commands like this are also listed when you run `cargo --list`. Being -able to `cargo install` extensions and then run them just like the built-in -Cargo tools is a super convenient benefit of Cargo’s design! +modify Cargo. If a binary in your `$PATH` is named `cargo-something`, you can +run it as if it was a Cargo subcommand by running `cargo something`. Custom +commands like this are also listed when you run `cargo --list`. Being able to +use `cargo install` to install extensions and then run them just like the +built-in Cargo tools is a super convenient benefit of Cargo’s design! ## Summary -Sharing code with Cargo and crates.io is part of what makes the Rust ecosystem -useful for many different tasks. Rust’s standard library is small and stable, -but crates are easy to share, use, and improve on a timeline different from the -language itself. Don’t be shy about sharing code that’s useful to you on -Crates.io; it’s likely that it will be useful to someone else as well! +Sharing code with Cargo and *https://crates.io/* is part of what makes the +Rust ecosystem useful for many different tasks. Rust’s standard library is +small and stable, but crates are easy to share, use, and improve on a timeline +different from the language. Don’t be shy about sharing code that’s useful to +you on *https://crates.io/*; it’s likely that it will be useful to someone +else as well! diff --git a/second-edition/src/ch14-00-more-about-cargo.md b/second-edition/src/ch14-00-more-about-cargo.md index 3a6da6ace..c6e66ef12 100644 --- a/second-edition/src/ch14-00-more-about-cargo.md +++ b/second-edition/src/ch14-00-more-about-cargo.md @@ -1,14 +1,15 @@ -# More about Cargo and Crates.io +# More About Cargo and Crates.io So far we’ve used only the most basic features of Cargo to build, run, and test -our code, but it can do a lot more. Here we’ll go over some of its other, more -advanced features to show you how to: +our code, but it can do a lot more. In this chapter, we’ll discuss some of its +other, more advanced features to show you how to: * Customize your build through release profiles -* Publish libraries on crates.io -* Organize larger projects with workspaces -* Install binaries from crates.io -* Extend Cargo with your own custom commands +* Publish libraries on [crates.io](https://crates.io) +* Organize large projects with workspaces +* Install binaries from [crates.io](https://crates.io) +* Extend Cargo using custom commands -Cargo can do even more than what we can cover in this chapter too, so for a -full explanation, see [its documentation](https://doc.rust-lang.org/cargo/). +Cargo can do even more than what we cover in this chapter, so for a full +explanation of all its features, see [its +documentation](https://doc.rust-lang.org/cargo/). diff --git a/second-edition/src/ch14-01-release-profiles.md b/second-edition/src/ch14-01-release-profiles.md index e269cf517..4e4f4cd67 100644 --- a/second-edition/src/ch14-01-release-profiles.md +++ b/second-edition/src/ch14-01-release-profiles.md @@ -1,18 +1,17 @@ ## Customizing Builds with Release Profiles -In Rust *release profiles* are pre-defined, and customizable, profiles with -different configurations, to allow the programmer more control over various -options for compiling your code. Each profile is configured independently of +In Rust, *release profiles* are predefined and customizable profiles with +different configurations that allow a programmer to have more control over +various options for compiling code. Each profile is configured independently of the others. -Cargo has two main profiles you should know about: the `dev` profile Cargo uses -when you run `cargo build`, and the `release` profile Cargo uses when you run -`cargo build --release`. The `dev` profile is defined with good defaults for -developing, and likewise the `release` profile has good defaults for release -builds. +Cargo has two main profiles: the `dev` profile Cargo uses when you run `cargo +build` and the `release` profile Cargo uses when you run `cargo build +--release`. The `dev` profile is defined with good defaults for developing, and +the `release` profile has good defaults for release builds. -These names may be familiar from the output of your builds, which shows the -profile used in the build: +These profile names might be familiar from the output of your builds, which +shows the profile used in the build: ```text $ cargo build @@ -21,16 +20,14 @@ $ cargo build --release Finished release [optimized] target(s) in 0.0 secs ``` -The “dev” and “release” notifications here indicate that the compiler is using -different profiles. - -### Customizing Release Profiles +The `dev` and `release` shown in this build output indicate that the compiler +is using different profiles. Cargo has default settings for each of the profiles that apply when there aren’t any `[profile.*]` sections in the project’s *Cargo.toml* file. By adding -`[profile.*]` sections for any profile we want to customize, we can choose to -override any subset of the default settings. For example, here are the default -values for the `opt-level` setting for the `dev` and `release` profiles: +`[profile.*]` sections for any profile we want to customize, we can override +any subset of the default settings. For example, here are the default values +for the `opt-level` setting for the `dev` and `release` profiles: Filename: Cargo.toml @@ -42,20 +39,20 @@ opt-level = 0 opt-level = 3 ``` -The `opt-level` setting controls how many optimizations Rust will apply to your -code, with a range of zero to three. Applying more optimizations makes -compilation take longer, so if you’re in development and compiling very often, -you’d want compiling to be fast at the expense of the resulting code running -slower. That’s why the default `opt-level` for `dev` is `0`. When you’re ready -to release, it’s better to spend more time compiling. You’ll only be compiling -in release mode once, and running the compiled program many times, so release -mode trades longer compile time for code that runs faster. That’s why the -default `opt-level` for the `release` profile is `3`. +The `opt-level` setting controls the number of optimizations Rust will apply to +your code with a range of zero to three. Applying more optimizations extends +compiling time, so if you’re in development and compiling your code often, you +want faster compiling even at the expense of the resulting code running slower. +That is the reason the default `opt-level` for `dev` is `0`. When you’re ready +to release your code, it’s best to spend more time compiling. You’ll only +compile in release mode once and run the compiled program many times, so +release mode trades longer compile time for code that runs faster. That is the +reason the default `opt-level` for the `release` profile is `3`. -We can choose to override any default setting by adding a different value for -them in *Cargo.toml*. If we wanted to use optimization level 1 in the -development profile, for example, we can add these two lines to our project’s -*Cargo.toml*: +We can override any default setting by adding a different value for it in +*Cargo.toml*. For example, if we want to use optimization level 1 in the +development profile, we can add these two lines to our project’s *Cargo.toml* +file: Filename: Cargo.toml @@ -64,10 +61,10 @@ development profile, for example, we can add these two lines to our project’s opt-level = 1 ``` -This overrides the default setting of `0`. Now when we run `cargo build`, Cargo -will use the defaults for the `dev` profile plus our customization to -`opt-level`. Because we set `opt-level` to `1`, Cargo will apply more -optimizations than the default, but not as many as a release build. +This code overrides the default setting of `0`. Now when we run `cargo` +`build`, Cargo will use the defaults for the `dev` profile plus our +customization to `opt-level`. Because we set `opt-level` to `1`, Cargo will +apply more optimizations than the default, but not as many as a release build. For the full list of configuration options and defaults for each profile, see [Cargo’s documentation](https://doc.rust-lang.org/cargo/). diff --git a/second-edition/src/ch14-02-publishing-to-crates-io.md b/second-edition/src/ch14-02-publishing-to-crates-io.md index 7d6d0ccc1..4a45ce076 100644 --- a/second-edition/src/ch14-02-publishing-to-crates-io.md +++ b/second-edition/src/ch14-02-publishing-to-crates-io.md @@ -1,29 +1,30 @@ ## Publishing a Crate to Crates.io -We’ve used packages from crates.io as dependencies of our project, but you can -also share your code for other people to use by publishing your own packages. -Crates.io distributes the source code of your packages, so it primarily hosts -code that’s open source. +We’ve used packages from [crates.io](https://crates.io) as +dependencies of our project, but you can also share your code for other people +to use by publishing your own packages. The crate registry at +[crates.io](https://crates.io) distributes the source code of +your packages, so it primarily hosts code that is open source. Rust and Cargo have features that help make your published package easier for -people to find and use. We’ll talk about some of those features, then cover how -to publish a package. +people to use and to find in the first place. We’ll talk about some of these +features next, and then explain how to publish a package. ### Making Useful Documentation Comments Accurately documenting your packages will help other users know how and when to -use them, so it’s worth spending some time to write documentation. In Chapter -3, we discussed how to comment Rust code with `//`. Rust also has particular -kind of comment for documentation, known conveniently as *documentation +use them, so it’s worth spending time writing documentation. In Chapter 3, we +discussed how to comment Rust code using `//`. Rust also has a particular kind +of comment for documentation, which is known conveniently as *documentation comments*, that will generate HTML documentation. The HTML displays the -contents of documentation comments for public API items, intended for -programmers interested in knowing how to *use* your crate, as opposed to how +contents of documentation comments for public API items intended for +programmers interested in knowing how to *use* your crate as opposed to how your crate is *implemented*. Documentation comments use `///` instead of `//` and support Markdown notation -for formatting the text if you’d like. You place documentation comments just -before the item they are documenting. Listing 14-1 shows documentation comments -for an `add_one` function in a crate named `my_crate`: +for formatting the text if you want to use it. You place documentation comments +just before the item they’re documenting. Listing 14-1 shows documentation +comments for an `add_one` function in a crate named `my_crate`: Filename: src/lib.rs @@ -45,18 +46,18 @@ pub fn add_one(x: i32) -> i32 { Listing 14-1: A documentation comment for a function -Here, we give a description of what the `add_one` function does, then start a -section with the heading “Examples”, and code that demonstrates how to use the -`add_one` function. We can generate the HTML documentation from this -documentation comment by running `cargo doc`. This command runs the `rustdoc` -tool distributed with Rust and puts the generated HTML documentation in the -*target/doc* directory. +Here, we give a description of what the `add_one` function does, start a +section with the heading `Examples`, and then provide code that demonstrates +how to use the `add_one` function. We can generate the HTML documentation from +this documentation comment by running `cargo doc`. This command runs the +`rustdoc` tool distributed with Rust and puts the generated HTML documentation +in the *target/doc* directory. For convenience, running `cargo doc --open` will build the HTML for your current crate’s documentation (as well as the documentation for all of your crate’s dependencies) and open the result in a web browser. Navigate to the -`add_one` function and you’ll see how the text in the documentation comments -gets rendered, shown here in Figure 14-1: +`add_one` function and you’ll see how the text in the documentation comments is +rendered, as shown in Figure 14-1: Rendered HTML documentation for the `add_one` function of `my_crate` @@ -65,35 +66,35 @@ function #### Commonly Used Sections -We used the `# Examples` markdown heading in Listing 14-1 to create a section -in the HTML with the title “Examples”. Some other sections that crate authors +We used the `# Examples` Markdown heading in Listing 14-1 to create a section +in the HTML with the title “Examples.” Some other sections that crate authors commonly use in their documentation include: -* **Panics**: The scenarios in which this function could `panic!`. Callers of - this function who don’t want their programs to panic should make sure that - they don’t call this function in these situations. -* **Errors**: If this function returns a `Result`, describing the kinds of +* **Panics**: The scenarios in which the function being documented could + `panic!`. Callers of the function who don’t want their programs to panic + should make sure they don’t call the function in these situations. +* **Errors**: If the function returns a `Result`, describing the kinds of errors that might occur and what conditions might cause those errors to be - returned can be helpful to callers so that they can write code to handle the + returned can be helpful to callers so they can write code to handle the different kinds of errors in different ways. -* **Safety**: If this function is `unsafe` to call (we will discuss unsafety in +* **Safety**: If the function is `unsafe` to call (we discuss unsafety in Chapter 19), there should be a section explaining why the function is unsafe - and covering the invariants that this function expects callers to uphold. + and covering the invariants that the function expects callers to uphold. -Most documentation comment sections don’t need all of these sections, but this -is a good list to check to remind you of the kinds of things that people +Most documentation comment sections don’t need all of these sections, but it’s +a good list to check to remind you of the aspects of your code that people calling your code will be interested in knowing about. #### Documentation Comments as Tests -Adding examples in code blocks in your documentation comments is a way to -clearly demonstrate how to use your library, but it has an additional bonus: -running `cargo test` will run the code examples in your documentation as tests! -Nothing is better than documentation with examples. Nothing is worse than -examples that don’t actually work because the code has changed since the -documentation has been written. Try running `cargo test` with the documentation -for the `add_one` function like in Listing 14-1; you should see a section in -the test results like this: +Adding examples in code blocks in your documentation comments can clearly +demonstrate how to use your library, and doing so has an additional bonus: +running `cargo test` will run the code examples in your documentation as +tests! Nothing is better than documentation with examples. But nothing is worse +than examples that don’t work because the code has changed since the +documentation was written. Run `cargo test` with the documentation for the +`add_one` function from Listing 14-1; you should see a section in the test +results like this: ```text Doc-tests my_crate @@ -101,25 +102,25 @@ the test results like this: running 1 test test src/lib.rs - add_one (line 5) ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` -Now try changing either the function or the example so that the `assert_eq!` in -the example will panic. Run `cargo test` again, and you’ll see that the doc -tests catch that the example and the code are out of sync from one another! +Now change either the function or the example so the `assert_eq!` in the +example panics. Run `cargo test` again; you’ll see that the doc tests catch +that the example and the code are out of sync from one another! #### Commenting Contained Items -There’s another style of doc comment, `//!`, that adds documentation to the -item that contains the comments, rather than adding documentation to the items -following the comments. These are typically used inside the crate root file +Another style of doc comment, `//!`, adds documentation to the item that +contains the comments rather than adding documentation to the items following +the comments. We typically use these doc comments inside the crate root file (*src/lib.rs* by convention) or inside a module to document the crate or the module as a whole. -For example, if we wanted to add documentation that described the purpose of -the `my_crate` crate that contains the `add_one` function, we can add -documentation comments that start with `//!` to the beginning of *src/lib.rs* -as shown in Listing 14-2: +For example, if we want to add documentation that describes the purpose of the +`my_crate` crate that contains the `add_one` function, we can add documentation +comments that start with `//!` to the beginning of the *src/lib.rs* file, as +shown in Listing 14-2: Filename: src/lib.rs @@ -142,7 +143,7 @@ that contains this comment rather than an item that follows this comment. In this case, the item that contains this comment is the *src/lib.rs* file, which is the crate root. These comments describe the entire crate. -If we run `cargo doc --open`, we’ll see these comments displayed on the front +When we run `cargo doc --open`, these comments will display on the front page of the documentation for `my_crate` above the list of public items in the crate, as shown in Figure 14-2: @@ -152,38 +153,38 @@ crate, as shown in Figure 14-2: including the comment describing the crate as a whole Documentation comments within items are useful for describing crates and -modules especially. Use them to talk about the purpose of the container overall -to help users of your crate understand your organization. +modules especially. Use them to explain the purpose of the container overall to +help your crate users understand your organization. -#### Exporting a Convenient Public API with `pub use` +### Exporting a Convenient Public API with `pub use` -In Chapter 7, we covered how to organize our code into modules with the `mod` -keyword, how to make items public with the `pub` keyword, and how to bring -items into a scope with the `use` keyword. The structure that makes sense to -you while you’re developing a crate may not be very convenient for your users, -however. You may wish to organize your structs in a hierarchy containing -multiple levels, but people that want to use a type you’ve defined deep in the +In Chapter 7, we covered how to organize our code into modules using the `mod` +keyword, how to make items public using the `pub` keyword, and how to bring +items into a scope with the `use` keyword. However, the structure that makes +sense to you while you’re developing a crate might not be very convenient for +your users. You might want to organize your structs in a hierarchy containing +multiple levels, but people who want to use a type you’ve defined deep in the hierarchy might have trouble finding out that those types exist. They might -also be annoyed at having to type `use -my_crate::some_module::another_module::UsefulType;` rather than `use -my_crate::UsefulType;`. +also be annoyed at having to enter `use` +`my_crate::some_module::another_module::UsefulType;` rather than `use` +`my_crate::UsefulType;`. The structure of your public API is a major consideration when publishing a crate. People who use your crate are less familiar with the structure than you -are, and might have trouble finding the pieces they want to use if the module -hierarchy is large. +are and might have difficulty finding the pieces they want to use if your crate +has a large module hierarchy. -The good news is that, if the structure *isn’t* convenient for others to use +The good news is that if the structure *isn’t* convenient for others to use from another library, you don’t have to rearrange your internal organization: -you can choose to re-export items to make a public structure that’s different -to your private structure, using `pub use`. Re-exporting takes a public item in -one location and makes it public in another location as if it was defined in -the other location instead. +instead, you can re-export items to make a public structure that’s different +than your private structure by using `pub use`. Re-exporting takes a public +item in one location and makes it public in another location, as if it was +defined in the other location instead. For example, say we made a library named `art` for modeling artistic concepts. -Within this library is a `kinds` module containing two enums named -`PrimaryColor` and `SecondaryColor` and a `utils` module containing a function -named `mix` as shown in Listing 14-3: +Within this library are two modules: a `kinds` module containing two enums +named `PrimaryColor` and `SecondaryColor`, and a `utils` module containing a +function named `mix`, as shown in Listing 14-3: Filename: src/lib.rs @@ -222,8 +223,8 @@ pub mod utils { Listing 14-3: An `art` library with items organized into `kinds` and `utils` modules -The front page of the documentation for this crate generated by `cargo doc` -would look like Figure 14-3: +Figure 14-3 shows what the front page of the documentation for this crate +generated by `cargo doc` would look like: Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules @@ -231,13 +232,13 @@ would look like Figure 14-3: that lists the `kinds` and `utils` modules Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on the -front page, nor is the `mix` function. We have to click on `kinds` and `utils` -in order to see them. +front page, nor is the `mix` function. We have to click `kinds` and `utils` to +see them. -Another crate depending on this library would need `use` statements that import -the items from `art` including specifying the module structure that’s currently -defined. Listing 14-4 shows an example of a crate that uses the `PrimaryColor` -and `mix` items from the `art` crate: +Another crate that depends on this library would need `use` statements that +import the items from `art`, including specifying the module structure that’s +currently defined. Listing 14-4 shows an example of a crate that uses the +`PrimaryColor` and `mix` items from the `art` crate: Filename: src/main.rs @@ -257,18 +258,19 @@ fn main() { Listing 14-4: A crate using the `art` crate’s items with its internal structure exported -The author of the code in Listing 14-4 that uses the `art` crate had to figure -out that `PrimaryColor` is in the `kinds` module and `mix` is in the `utils` -module. The module structure of the `art` crate is more relevant to developers -working on the `art` crate than developers using the `art` crate. The internal -structure that organizes parts of the crate into the `kinds` module and the -`utils` module doesn’t add any useful information to someone trying to -understand how to use the `art` crate. The `art` crate’s module structure adds -confusion in having to figure out where to look and inconvenience in having to +The author of the code in Listing 14-4, which uses the `art` crate, had to +figure out that `PrimaryColor` is in the `kinds` module and `mix` is in the +`utils` module. The module structure of the `art` crate is more relevant to +developers working on the `art` crate than developers using the `art` crate. +The internal structure that organizes parts of the crate into the `kinds` +module and the `utils` module doesn’t contain any useful information for +someone trying to understand how to use the `art` crate. Instead, the `art` +crate’s module structure causes confusion because developers have to figure out +where to look, and the structure is inconvenient because developers must specify the module names in the `use` statements. -To remove the internal organization from the public API, we can take the `art` -crate code from Listing 14-3 and add `pub use` statements to re-export the +To remove the internal organization from the public API, we can modify the +`art` crate code in Listing 14-3 to add `pub use` statements to re-export the items at the top level, as shown in Listing 14-5: Filename: src/lib.rs @@ -294,18 +296,18 @@ pub mod utils { Listing 14-5: Adding `pub use` statements to re-export items -The API documentation generated with `cargo doc` for this crate will now list -and link re-exports on the front page as shown in Figure 14-4, which makes -these types easier to find. +The API documentation that `cargo doc` generates for this crate will now list +and link re-exports on the front page, as shown in Figure 14-4, which makes the +`PrimaryColor` and `SecondaryColor` types and the `mix` function easier to find: Rendered documentation for the `art` crate with the re-exports on the front page Figure 14-4: Front page of the documentation for `art` that lists the re-exports -Users of the `art` crate can still see and choose to use the internal structure -as in Listing 14-3, or they can use the more convenient structure from Listing -14-5, as shown in Listing 14-6: +The `art` crate users can still see and use the internal structure from Listing +14-3 as demonstrated in Listing 14-4, or they can use the more convenient +structure in Listing 14-5, as shown in Listing 14-6: Filename: src/main.rs @@ -324,48 +326,50 @@ fn main() { the `art` crate In cases where there are many nested modules, re-exporting the types at the top -level with `pub use` can make a big difference in the experience of people who -use the crate. +level with `pub use` can make a significant difference in the experience of +people who use the crate. Creating a useful public API structure is more of an art than a science, and -you can iterate to find the API that works best for your users. Choosing `pub -use` gives you flexibility in how you structure your crate internally, and -decouples that internal structure with what you present to your users. Take a -look at some of the code of crates you’ve installed to see if their internal -structure differs from their public API. +you can iterate to find the API that works best for your users. Choosing `pub` +`use` gives you flexibility in how you structure your crate internally and +decouples that internal structure with what you present to your users. Look at +some of the code of crates you’ve installed to see if their internal structure +differs from their public API. -### Setting up a Crates.io Account +### Setting Up a Crates.io Account -Before you can publish any crates, you need to create an account on crates.io -and get an API token. To do so, visit the home page at *https://crates.io* and -log in via a GitHub account—the GitHub account is a requirement for now, but -the site may support other ways of creating an account in the future. Once -you’re logged in, visit your account settings at *https://crates.io/me* and -retrieve your API key. Then run the `cargo login` command with your API key, -like this: +Before you can publish any crates, you need to create an account on +[crates.io](https://crates.io) and get an API token. To do so, +visit the home page at [crates.io](https://crates.io) and log in +via a GitHub account: the GitHub account is currently a requirement, but the +site might support other ways of creating an account in the future. Once you’re +logged in, visit your account settings at +[https://crates.io/me/](https://crates.io/me/) and retrieve your +API key. Then run the `cargo` `login` command with your API key, like this: ```text $ cargo login abcdefghijklmnopqrstuvwxyz012345 ``` This command will inform Cargo of your API token and store it locally in -*~/.cargo/credentials*. Note that this token is a *secret* and should not be -shared with anyone else. If it is shared with anyone for any reason, you should -revoke it and generate a new token on Crates.io. +*~/.cargo/credentials*. Note that this token is a *secret*: do not share it +with anyone else. If you do share it with anyone for any reason, you should +revoke it and generate a new token on [crates.io](https://crates.io). ### Before Publishing a New Crate -Now you have an account, and let’s say you already have a crate you want to -publish. Before publishing, you’ll need to add some metadata to your crate by -adding it to the `[package]` section of the crate’s *Cargo.toml*. +Now that you have an account, let’s say you have a crate you want to publish. +Before publishing, you’ll need to add some metadata to your crate by adding it +to the `[package]` section of the crate’s *Cargo.toml* file. -Your crate will first need a unique name. While you’re working on a crate -locally, you may name a crate whatever you’d like. However, crate names on -Crates.io are allocated on a first-come-first-serve basis. Once a crate name is -taken, no one else may publish a crate with that name. Search for the name -you’d like to use on the site to find out if it has been taken. If it hasn’t, -edit the name in *Cargo.toml* under `[package]` to have the name you want to -use for publishing like so: +Your crate will need a unique name. While you’re working on a crate locally, +you can name a crate whatever you’d like. However, crate names on +[crates.io](https://crates.io) are allocated on a first-come, +first-served basis. Once a crate name is taken, no one else can publish a crate +with that name. Search for the name you want to use on the site to find out if +it has been used. If it hasn’t, edit the name in the *Cargo.toml* file under +`[package]` to use the name for publishing, like so: Filename: Cargo.toml @@ -374,8 +378,8 @@ use for publishing like so: name = "guessing_game" ``` -Even if you’ve chosen a unique name, if you try to run `cargo publish` to -publish the crate at this point, you’ll get a warning and then an error: +Even if you’ve chosen a unique name, when you run `cargo publish` to publish +the crate at this point, you’ll get a warning and then an error: ```text $ cargo publish @@ -386,17 +390,17 @@ homepage or repository. error: api errors: missing or empty metadata fields: description, license. ``` -This is because we’re missing some crucial information: a description and -license are required so that people will know what your crate does and under -what terms they may use it. To rectify this error, we need to include this -information in *Cargo.toml*. +The reason is that you’re missing some crucial information: a description and +license are required so people will know what your crate does and under what +terms they can use it. To rectify this error, you need to include this +information in the *Cargo.toml* file. -Make a description that’s just a sentence or two, as it will appear with your -crate in search results and on your crate’s page. For the `license` field, you -need to give a *license identifier value*. The Linux Foundation’s Software -Package Data Exchange (SPDX) at *http://spdx.org/licenses/* lists the -identifiers you can use for this value. For example, to specify that you’ve -licensed your crate using the MIT License, add the `MIT` identifier: +Add a description that is just a sentence or two, because it will appear with +your crate in search results. For the `license` field, you need to give a +*license identifier value*. The Linux Foundation’s Software Package Data +Exchange (SPDX) at *http://spdx.org/licenses/* lists the identifiers you can +use for this value. For example, to specify that you’ve licensed your crate +using the MIT License, add the `MIT` identifier: Filename: Cargo.toml @@ -407,19 +411,19 @@ license = "MIT" ``` If you want to use a license that doesn’t appear in the SPDX, you need to place -the text of that license in a file, include the file in your project, then use -`license-file` to specify the name of that file instead of using the `license` -key. +the text of that license in a file, include the file in your project, and then +use `license-file` to specify the name of that file instead of using the +`license` key. -Guidance on which license is right for your project is out of scope for this -book. Many people in the Rust community choose to license their projects in the -same way as Rust itself, with a dual license of `MIT/Apache-2.0`—this +Guidance on which license is appropriate for your project is beyond the scope +of this book. Many people in the Rust community license their projects in the +same way as Rust by using a dual license of `MIT OR Apache-2.0`, which demonstrates that you can also specify multiple license identifiers separated -by a slash. +by `OR` to have multiple licenses for your project. -So, with a unique name, the version, and author details that `cargo new` added -when you created the crate, your description, and the license you chose added, -the *Cargo.toml* for a project that’s ready to publish might look like this: +With a unique name, the version, the author details that `cargo new` added +when you created the crate, your description, and a license added, the +*Cargo.toml* file for a project that is ready to publish might look like this: Filename: Cargo.toml @@ -429,29 +433,31 @@ name = "guessing_game" version = "0.1.0" authors = ["Your Name "] description = "A fun game where you guess what number the computer has chosen." -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" [dependencies] ``` [Cargo’s documentation](https://doc.rust-lang.org/cargo/) describes other -metadata you can specify to ensure your crate can be discovered and used more +metadata you can specify to ensure others can discover and use your crate more easily! ### Publishing to Crates.io Now that you’ve created an account, saved your API token, chosen a name for your crate, and specified the required metadata, you’re ready to publish! -Publishing a crate uploads a specific version to crates.io for others to use. +Publishing a crate uploads a specific version to +[crates.io](https://crates.io) for others to use. -Take care when publishing a crate, because a publish is *permanent*. The +Be careful when publishing a crate because a publish is *permanent*. The version can never be overwritten, and the code cannot be deleted. One major -goal of Crates.io is to act as a permanent archive of code so that builds of -all projects that depend on crates from Crates.io will continue to work. -Allowing deletion of versions would make fulfilling that goal impossible. -However, there is no limit to the number of versions of a crate you can publish. +goal of [crates.io](https://crates.io) is to act as a permanent +archive of code so that builds of all projects that depend on crates from +[crates.io](https://crates.io) will continue to work. Allowing +version deletions would make fulfilling that goal impossible. However, there is +no limit to the number of crate versions you can publish. -Let’s run the `cargo publish` command again. It should succeed now: +Run the `cargo publish` command again. It should succeed now: ```text $ cargo publish @@ -470,24 +476,24 @@ anyone can easily add your crate as a dependency of their project. ### Publishing a New Version of an Existing Crate When you’ve made changes to your crate and are ready to release a new version, -you change the `version` value specified in your *Cargo.toml* and republish. -Use the [Semantic Versioning rules][semver] to decide what an appropriate next -version number is based on the kinds of changes you’ve made. Then run `cargo -publish` to upload the new version. +you change the `version` value specified in your *Cargo.toml* file and +republish. Use the [Semantic Versioning rules][semver] to decide what an +appropriate next version number is based on the kinds of changes you’ve made. +Then run `cargo publish` to upload the new version. [semver]: http://semver.org/ ### Removing Versions from Crates.io with `cargo yank` -While you can’t remove previous versions of a crate, you can prevent any future -projects from adding them as a new dependency. This is useful when a version of -a crate ends up being broken for one reason or another. For situations such as -this, Cargo supports *yanking* a version of a crate. +Although you can’t remove previous versions of a crate, you can prevent any +future projects from adding them as a new dependency. This is useful when a +crate version is broken for one reason or another. In such situations, Cargo +supports *yanking* a crate version. Yanking a version prevents new projects from starting to depend on that version while allowing all existing projects that depend on it to continue to download and depend on that version. Essentially, a yank means that all projects with a -*Cargo.lock* will not break, while any future *Cargo.lock* files generated will +*Cargo.lock* will not break, and any future *Cargo.lock* files generated will not use the yanked version. To yank a version of a crate, run `cargo yank` and specify which version you @@ -497,13 +503,13 @@ want to yank: $ cargo yank --vers 1.0.1 ``` -You can also undo a yank, and allow projects to start depending on a version -again, by adding `--undo` to the command: +By adding `--undo` to the command, you can also undo a yank and allow projects +to start depending on a version again: ```text $ cargo yank --vers 1.0.1 --undo ``` -A yank *does not* delete any code. The yank feature is not intended for -deleting accidentally uploaded secrets, for example. If that happens, you must +A yank *does not* delete any code. For example, the yank feature is not +intended for deleting accidentally uploaded secrets. If that happens, you must reset those secrets immediately. diff --git a/second-edition/src/ch14-03-cargo-workspaces.md b/second-edition/src/ch14-03-cargo-workspaces.md index 747ca299a..0a40064d6 100644 --- a/second-edition/src/ch14-03-cargo-workspaces.md +++ b/second-edition/src/ch14-03-cargo-workspaces.md @@ -1,19 +1,18 @@ ## Cargo Workspaces -In Chapter 12, we built a package that included both a binary crate and a -library crate. You may find, as your project develops, that the library crate -continues to get bigger and you want to split your package up further into -multiple library crates. In this situation, Cargo has a feature called +In Chapter 12, we built a package that included a binary crate and a library +crate. As your project develops, you might find that the library crate +continues to get bigger and you want to split up your package further into +multiple library crates. In this situation, Cargo offers a feature called *workspaces* that can help manage multiple related packages that are developed in tandem. -A *workspace* is a set of packages that will all share the same *Cargo.lock* -and output directory. Let’s make a project using a workspace, using trivial -code so we can concentrate on the structure of a workspace. We’ll have a binary -that uses two libraries: one library that will provide an `add_one` function -and a second library that will provide an `add_two` function. These three -crates will all be part of the same workspace. We’ll start by creating a new -crate for the binary: +A *workspace* is a set of packages that share the same *Cargo.lock* and output +directory. Let’s make a project using a workspace and use trivial code so we +can concentrate on the structure of the workspace. We’ll have a binary that +uses two libraries: one library that provides an `add_one` function and a +second library that provides an `add_two` function. These three crates will be +part of the same workspace. We’ll start by creating a new crate for the binary: ```text $ cargo new --bin adder @@ -21,9 +20,9 @@ $ cargo new --bin adder $ cd adder ``` -We need to modify the binary package’s *Cargo.toml* and add a `[workspace]` -section to tell Cargo the `adder` package is a workspace. Add this at the -bottom of the file: +We need to modify the binary package’s *Cargo.toml* file and add a +`[workspace]` section to tell Cargo the `adder` package is a workspace. Add the +following to the bottom of the file: Filename: Cargo.toml @@ -31,22 +30,22 @@ bottom of the file: [workspace] ``` -Like many Cargo features, workspaces support convention over configuration: we -don’t need to add anything more than this to *Cargo.toml* to define our -workspace as long as we follow the convention. +As with many Cargo features, workspaces support convention over configuration: +we don’t need to add anything more than this to the *Cargo.toml* file to define +our workspace as long as we follow the convention. ### Specifying Workspace Dependencies -By default, Cargo will include all transitive path dependencies. A *path -dependency* is when any crate, whether in a workspace or not, specifies that it -has a dependency on a crate in a local directory by using the `path` attribute -on the dependency specification in *Cargo.toml*. If a crate has the -`[workspace]` key, or if the crate is itself part of a workspace, and we -specify path dependencies where the paths are subdirectories of the crate’s -directory, those dependent crates will be considered part of the workspace. -Let’s specify in the *Cargo.toml* for the top-level `adder` crate that it will -have a dependency on an `add-one` crate that will be in the `add-one` -subdirectory, by changing *Cargo.toml* to look like this: +By default, Cargo includes all transitive path dependencies. A *path +dependency* is used when any crate, whether in a workspace or not, specifies +that it has a dependency on a crate in a local directory by using the `path` +attribute on the dependency specification in the *Cargo.toml* file. If a crate +has the `[workspace]` key or if the crate is part of a workspace and we specify +path dependencies where the paths are subdirectories of the crate’s directory, +those dependent crates will be considered part of the workspace. Let’s specify +in the *Cargo.toml* file for the top-level `adder` crate that it will have a +dependency on an `add-one` crate that will be in the `add-one` subdirectory by +changing the *Cargo.toml* file to look like this: Filename: Cargo.toml @@ -55,20 +54,21 @@ subdirectory, by changing *Cargo.toml* to look like this: add-one = { path = "add-one" } ``` -If we add dependencies to *Cargo.toml* that don’t have a `path` specified, -those dependencies will be normal dependencies that aren’t in this workspace -and are assumed to come from Crates.io. +If we add dependencies to the *Cargo.toml* file that don’t have a `path` +specified, those dependencies will be normal dependencies that aren’t in this +workspace and are assumed to come from [crates.io](https://crates.io). ### Creating the Second Crate in the Workspace -Next, while in the `adder` directory, generate an `add-one` crate: +Next, while in the *adder* directory, generate an `add-one` crate: ```text $ cargo new add-one Created library `add-one` project ``` -Your `adder` directory should now have these directories and files: +Your *adder* directory should now have these directories and files: ```text ├── Cargo.toml @@ -80,7 +80,7 @@ Your `adder` directory should now have these directories and files: └── main.rs ``` -In *add-one/src/lib.rs*, let’s add an `add_one` function: +In the *add-one/src/lib.rs* file, let’s add an `add_one` function: Filename: add-one/src/lib.rs @@ -90,9 +90,9 @@ pub fn add_one(x: i32) -> i32 { } ``` -Open up *src/main.rs* for `adder` and add an `extern crate` line at the top of -the file to bring the new `add-one` library crate into scope. Then change the -`main` function to call the `add_one` function, as in Listing 14-7: +Open the *src/main.rs* file for `adder` and add an `extern crate` line at the +top to bring the new `add-one` library crate into scope. Then change the `main` +function to call the `add_one` function, as in Listing 14-7: Filename: src/main.rs @@ -108,7 +108,8 @@ fn main() { Listing 14-7: Using the `add-one` library crate from the `adder` crate -Let’s build the `adder` crate by running `cargo build` in the *adder* directory! +Let’s build the `adder` crate by running `cargo build` in the *adder* +directory! ```text $ cargo build @@ -117,7 +118,7 @@ $ cargo build Finished dev [unoptimized + debuginfo] target(s) in 0.68 secs ``` -Note that this builds both the `adder` crate and the `add-one` crate in +Note that this builds the `adder` crate and the `add-one` crate in *adder/add-one*. Now your *adder* directory should have these files: ```text @@ -132,29 +133,27 @@ Note that this builds both the `adder` crate and the `add-one` crate in └── target ``` -The workspace has one *target* directory at the top level; *add-one* doesn’t -have its own *target* directory. Even if we go into the `add-one` directory and -run `cargo build`, the compiled artifacts end up in *adder/target* rather than -*adder/add-one/target*. The crates in a workspace depend on each other. If each -crate had its own *target* directory, each crate in the workspace would have to -recompile each other crate in the workspace in order to have the artifacts in -its own *target* directory. By sharing one *target* directory, the crates in -the workspace can avoid rebuilding the other crates in the workspace more than -necessary. +The workspace has one *target* directory at the top level; the `add-one` crate +doesn’t have its own *target* directory. Even if we go into the *add-one* +directory and run `cargo build`, the compiled artifacts end up in +*adder/target* rather than *adder/add-one/target*. The crates in a workspace +depend on each other. If each crate had its own *target* directory, each crate +in the workspace would have to recompile each of the other crates in the +workspace to have the artifacts in its own *target* directory. By sharing one +*target* directory, the crates in the workspace can avoid rebuilding the other +crates in the workspace more than necessary. #### Depending on an External Crate in a Workspace -Also notice the workspace only has one *Cargo.lock*, rather than having a +Notice that the workspace has only one *Cargo.lock* rather than having a top-level *Cargo.lock* and *add-one/Cargo.lock*. This ensures that all crates are using the same version of all dependencies. If we add the `rand` crate to -both *Cargo.toml* and *add-one/Cargo.toml*, Cargo will resolve both of those to -one version of `rand` and record that in the one *Cargo.lock*. Making all -crates in the workspace use the same dependencies means the crates in the -workspace will always be compatible with each other. Let’s try this out now. - -Let’s add the `rand` crate to the `[dependencies]` section in -*add-one/Cargo.toml* in order to be able to use the `rand` crate in the -`add-one` crate: +the *Cargo.toml* and *add-one/Cargo.toml* files, Cargo will resolve both of +those to one version of `rand` and record that in the one *Cargo.lock*. Making +all crates in the workspace use the same dependencies means the crates in the +workspace will always be compatible with each other. Let’s add the `rand` crate +to the `[dependencies]` section in the *add-one/Cargo.toml* file to be able to +use the `rand` crate in the `add-one` crate: Filename: add-one/Cargo.toml @@ -164,9 +163,9 @@ Let’s add the `rand` crate to the `[dependencies]` section in rand = "0.3.14" ``` -We can now add `extern crate rand;` to *add-one/src/lib.rs*, and building the -whole workspace by running `cargo build` in the *adder* directory will bring in -and compile the `rand` crate: +We can now add `extern crate rand;` to the *add-one/src/lib.rs* file, and +building the whole workspace by running `cargo build` in the *adder* +directory will bring in and compile the `rand` crate: ```text $ cargo build @@ -179,11 +178,12 @@ $ cargo build Finished dev [unoptimized + debuginfo] target(s) in 10.18 secs ``` -The top level *Cargo.lock* now contains information about `add-one`’s -dependency on `rand`. However, even though `rand` is used somewhere in the +The top-level *Cargo.lock* now contains information about the dependency of +`add-one` on `rand`. However, even though `rand` is used somewhere in the workspace, we can’t use it in other crates in the workspace unless we add -`rand` to their *Cargo.toml* as well. If we add `extern crate rand;` to -*src/main.rs* for the top level `adder` crate, for example, we’ll get an error: +`rand` to their *Cargo.toml* files as well. For example, if we add `extern` +`crate rand;` to the *src/main.rs* file for the top-level `adder` crate, +we’ll get an error: ```text $ cargo build @@ -195,13 +195,13 @@ error[E0463]: can't find crate for `rand` | ^^^^^^^^^^^^^^^^^^^ can't find crate ``` -To fix this, edit *Cargo.toml* for the top level `adder` crate and indicate -that `rand` is a dependency for that crate as well. Building the `adder` crate -will add `rand` to the list of dependencies for `adder` in *Cargo.lock*, but no -additional copies of `rand` will be downloaded. Cargo has ensured for us that -any crate in the workspace using the `rand` crate will be using the same -version. Using the same version of `rand` across the workspace saves space -since we won’t have multiple copies and ensures that the crates in the +To fix this, edit the *Cargo.toml* file for the top-level `adder` crate and +indicate that `rand` is a dependency for that crate as well. Building the +`adder` crate will add `rand` to the list of dependencies for `adder` in +*Cargo.lock*, but no additional copies of `rand` will be downloaded. Cargo has +ensured that any crate in the workspace using the `rand` crate will be using +the same version. Using the same version of `rand` across the workspace saves +space because we won’t have multiple copies and ensures that the crates in the workspace will be compatible with each other. #### Adding a Test to a Workspace @@ -240,9 +240,9 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured ``` -Wait a second, zero tests? We just added one! If we look at the output, we can -see that `cargo test` in a workspace only runs tests for the top level crate. -To run tests for all of the crates in the workspace, we need to pass the +Wait a second, zero tests? We just added one! When we look at the output, we +can see that `cargo test` in a workspace only runs tests for the top-level +crate. To run tests for all the crates in the workspace, we need to pass the `--all` flag: ```text @@ -268,10 +268,9 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` -When passing `--all`, `cargo test` will run the tests for all of the crates in -the workspace. We can also choose to run tests for one particular crate in a -workspace from the top level directory by using the `-p` flag and specifying -the name of the crate we want to test: +We can also run tests for one particular crate in a workspace from the +top-level directory by using the `-p` flag and specifying the name of the crate +we want to test: ```text $ cargo test -p add-one @@ -293,16 +292,16 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out This output shows `cargo test` only ran the tests for the `add-one` crate and didn’t run the `adder` crate tests. -If you choose to publish the crates in the workspace to crates.io, each crate -in the workspace will get published separately. The `cargo publish` command -does not have an `--all` flag or a `-p` flag, so it is necessary to change to -each crate’s directory and run `cargo publish` on each crate in the workspace -in order to publish them. +If you publish the crates in the workspace to +[crates.io](https://crates.io), each crate in the workspace will +need to be published separately. The `cargo` `publish` command does not have an +`--all` flag or a `-p` flag, so you must change to each crate’s directory and +run `cargo publish` on each crate in the workspace to publish them. -Now try adding an `add-two` crate to this workspace in a similar way as the -`add-one` crate for some more practice! +For additional practice, add an `add-two` crate to this workspace in a similar +way as the `add-one` crate! -As your project grows, consider using a workspace: smaller components are -easier to understand individually than one big blob of code. Keeping the crates -in a workspace can make coordination among them easier if they work together -and are often changed at the same time. +As your project grows, consider using a workspace: it’s easier to understand +smaller, individual components than one big blob of code. Keeping the crates in +a workspace can make coordination between them easier if they are often changed +at the same time. diff --git a/second-edition/src/ch14-04-installing-binaries.md b/second-edition/src/ch14-04-installing-binaries.md index 292766b33..7e6814755 100644 --- a/second-edition/src/ch14-04-installing-binaries.md +++ b/second-edition/src/ch14-04-installing-binaries.md @@ -3,21 +3,23 @@ The `cargo install` command allows you to install and use binary crates locally. This isn’t intended to replace system packages; it’s meant to be a convenient way for Rust developers to install tools that others have shared on -crates.io. Only packages that have binary targets can be installed. A binary -target is the runnable program that gets created if the crate has a -*src/main.rs* or another file specified as a binary, as opposed to a library -target that isn’t runnable on its own but is suitable for including within -other programs. Usually, crates have information in the *README* file about -whether a crate is a library, has a binary target, or both. +[crates.io](https://crates.io). You can only install packages +that have binary targets. A binary target is the runnable program that is +created if the crate has a *src/main.rs* file or another file specified as a +binary, as opposed to a library target that isn’t runnable on its own but is +suitable for including within other programs. Usually, crates have information +in the *README* file about whether a crate is a library, has a binary target, +or both. -All binaries from `cargo install` are put into the installation root’s *bin* -folder. If you installed Rust using *rustup.rs* and don’t have any custom -configurations, this will be `$HOME/.cargo/bin`. Ensure that directory is in -your `$PATH` to be able to run programs you’ve gotten through `cargo install`. +All binaries installed with `cargo install` are stored in the installation +root’s *bin* folder. If you installed Rust using *rustup.rs* and don’t have any +custom configurations, this directory will be *$HOME/.cargo/bin*. Ensure that +directory is in your `$PATH` to be able to run programs you’ve installed with +`cargo install`. -For example, we mentioned in Chapter 12 that there’s a Rust implementation of -the `grep` tool for searching files called `ripgrep`. If we want to install -`ripgrep`, we can run: +For example, in Chapter 12 we mentioned that there’s a Rust implementation of +the `grep` tool called `ripgrep` for searching files. If we want to install +`ripgrep`, we can run the following: ```text $ cargo install ripgrep @@ -31,5 +33,5 @@ Updating registry `https://github.com/rust-lang/crates.io-index` The last line of the output shows the location and the name of the installed binary, which in the case of `ripgrep` is `rg`. As long as the installation -directory is in your `$PATH` as mentioned above, you can then run `rg --help` -and start using a faster, rustier tool for searching files! +directory is in your `$PATH`, as mentioned previously, you can then run `rg` +`--help` and start using a faster, rustier tool for searching files! diff --git a/second-edition/src/ch14-05-extending-cargo.md b/second-edition/src/ch14-05-extending-cargo.md index 20da49bef..ba5ade832 100644 --- a/second-edition/src/ch14-05-extending-cargo.md +++ b/second-edition/src/ch14-05-extending-cargo.md @@ -1,16 +1,17 @@ ## Extending Cargo with Custom Commands Cargo is designed so you can extend it with new subcommands without having to -modify Cargo itself. If a binary in your `$PATH` is named `cargo-something`, -you can run it as if it were a Cargo subcommand by running `cargo something`. -Custom commands like this are also listed when you run `cargo --list`. Being -able to `cargo install` extensions and then run them just like the built-in -Cargo tools is a super convenient benefit of Cargo’s design! +modify Cargo. If a binary in your `$PATH` is named `cargo-something`, you can +run it as if it was a Cargo subcommand by running `cargo something`. Custom +commands like this are also listed when you run `cargo --list`. Being able to +use `cargo install` to install extensions and then run them just like the +built-in Cargo tools is a super convenient benefit of Cargo’s design! ## Summary -Sharing code with Cargo and crates.io is part of what makes the Rust ecosystem -useful for many different tasks. Rust’s standard library is small and stable, -but crates are easy to share, use, and improve on a timeline different from the -language itself. Don’t be shy about sharing code that’s useful to you on -Crates.io; it’s likely that it will be useful to someone else as well! +Sharing code with Cargo and [crates.io](https://crates.io) is +part of what makes the Rust ecosystem useful for many different tasks. Rust’s +standard library is small and stable, but crates are easy to share, use, and +improve on a timeline different from the language. Don’t be shy about sharing +code that’s useful to you on [crates.io](https://crates.io); +it’s likely that it will be useful to someone else as well!