]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | |
2 | [TOC] | |
3 | ||
4 | # More about Cargo and Crates.io | |
5 | ||
3b2f2976 XL |
6 | So far we’ve used only the most basic features of Cargo to build, run, and test |
7 | our code, but it can do a lot more. Here we’ll go over some of its other, more | |
8 | advanced features to show you how to: | |
cc61c64b | 9 | |
3b2f2976 XL |
10 | * Customize your build through release profiles |
11 | * Publish libraries on crates.io | |
12 | * Organize larger projects with workspaces | |
13 | * Install binaries from crates.io | |
14 | * Extend Cargo with your own custom commands | |
cc61c64b | 15 | |
3b2f2976 | 16 | Cargo can do even more than what we can cover in this chapter too, so for a |
ea8adc8c | 17 | full explanation, see its documentation at *https://doc.rust-lang.org/cargo/*. |
cc61c64b | 18 | |
3b2f2976 | 19 | ## Customizing Builds with Release Profiles |
cc61c64b | 20 | |
3b2f2976 XL |
21 | In Rust *release profiles* are pre-defined, and customizable, profiles with |
22 | different configurations, to allow the programmer more control over various | |
23 | options for compiling your code. Each profile is configured independently of | |
24 | the others. | |
25 | ||
ea8adc8c XL |
26 | Cargo has two main profiles you should know about: the `dev` profile Cargo uses |
27 | when you run `cargo build`, and the `release` profile Cargo uses when you run | |
28 | `cargo build --release`. The `dev` profile is defined with good defaults for | |
29 | developing, and likewise the `release` profile has good defaults for release | |
30 | builds. | |
3b2f2976 | 31 | |
ea8adc8c XL |
32 | These names may be familiar from the output of your builds, which shows the |
33 | profile used in the build: | |
3b2f2976 XL |
34 | |
35 | ``` | |
cc61c64b | 36 | $ cargo build |
3b2f2976 | 37 | Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs |
cc61c64b XL |
38 | $ cargo build --release |
39 | Finished release [optimized] target(s) in 0.0 secs | |
40 | ``` | |
41 | ||
ea8adc8c XL |
42 | The “dev” and “release” notifications here indicate that the compiler is using |
43 | different profiles. | |
cc61c64b | 44 | |
3b2f2976 | 45 | ### Customizing Release Profiles |
cc61c64b | 46 | |
3b2f2976 XL |
47 | Cargo has default settings for each of the profiles that apply when there |
48 | aren’t any `[profile.*]` sections in the project’s *Cargo.toml* file. By adding | |
49 | `[profile.*]` sections for any profile we want to customize, we can choose to | |
50 | override any subset of the default settings. For example, here are the default | |
51 | values for the `opt-level` setting for the `dev` and `release` profiles: | |
52 | ||
ea8adc8c XL |
53 | Filename: Cargo.toml |
54 | ||
3b2f2976 | 55 | ``` |
cc61c64b XL |
56 | [profile.dev] |
57 | opt-level = 0 | |
58 | ||
59 | [profile.release] | |
60 | opt-level = 3 | |
61 | ``` | |
62 | ||
63 | The `opt-level` setting controls how many optimizations Rust will apply to your | |
3b2f2976 XL |
64 | code, with a range of zero to three. Applying more optimizations makes |
65 | compilation take longer, so if you’re in development and compiling very often, | |
66 | you’d want compiling to be fast at the expense of the resulting code running | |
67 | slower. That’s why the default `opt-level` for `dev` is `0`. When you’re ready | |
68 | to release, it’s better to spend more time compiling. You’ll only be compiling | |
69 | in release mode once, and running the compiled program many times, so release | |
70 | mode trades longer compile time for code that runs faster. That’s why the | |
71 | default `opt-level` for the `release` profile is `3`. | |
72 | ||
73 | We can choose to override any default setting by adding a different value for | |
74 | them in *Cargo.toml*. If we wanted to use optimization level 1 in the | |
75 | development profile, for example, we can add these two lines to our project’s | |
76 | *Cargo.toml*: | |
77 | ||
3b2f2976 | 78 | Filename: Cargo.toml |
cc61c64b | 79 | |
3b2f2976 | 80 | ``` |
cc61c64b XL |
81 | [profile.dev] |
82 | opt-level = 1 | |
83 | ``` | |
84 | ||
3b2f2976 XL |
85 | This overrides the default setting of `0`. Now when we run `cargo build`, Cargo |
86 | will use the defaults for the `dev` profile plus our customization to | |
87 | `opt-level`. Because we set `opt-level` to `1`, Cargo will apply more | |
88 | optimizations than the default, but not as many as a release build. | |
cc61c64b | 89 | |
3b2f2976 | 90 | For the full list of configuration options and defaults for each profile, see |
ea8adc8c | 91 | Cargo’s documentation at *https://doc.rust-lang.org/cargo/*. |
cc61c64b XL |
92 | |
93 | ## Publishing a Crate to Crates.io | |
94 | ||
3b2f2976 XL |
95 | We’ve used packages from crates.io as dependencies of our project, but you can |
96 | also share your code for other people to use by publishing your own packages. | |
97 | Crates.io distributes the source code of your packages, so it primarily hosts | |
98 | code that’s open source. | |
cc61c64b | 99 | |
3b2f2976 XL |
100 | Rust and Cargo have features that help make your published package easier for |
101 | people to find and use. We’ll talk about some of those features, then cover how | |
102 | to publish a package. | |
cc61c64b | 103 | |
3b2f2976 | 104 | ### Making Useful Documentation Comments |
cc61c64b | 105 | |
3b2f2976 XL |
106 | Accurately documenting your packages will help other users know how and when to |
107 | use them, so it’s worth spending some time to write documentation. In Chapter | |
108 | 3, we discussed how to comment Rust code with `//`. Rust also has particular | |
109 | kind of comment for documentation, known conveniently as *documentation | |
110 | comments*, that will generate HTML documentation. The HTML displays the | |
111 | contents of documentation comments for public API items, intended for | |
112 | programmers interested in knowing how to *use* your crate, as opposed to how | |
113 | your crate is *implemented*. | |
cc61c64b | 114 | |
3b2f2976 XL |
115 | Documentation comments use `///` instead of `//` and support Markdown notation |
116 | for formatting the text if you’d like. You place documentation comments just | |
ea8adc8c | 117 | before the item they are documenting. Listing 14-1 shows documentation comments |
3b2f2976 | 118 | for an `add_one` function in a crate named `my_crate`: |
cc61c64b | 119 | |
3b2f2976 | 120 | Filename: src/lib.rs |
cc61c64b | 121 | |
3b2f2976 | 122 | ``` |
cc61c64b XL |
123 | /// Adds one to the number given. |
124 | /// | |
125 | /// # Examples | |
126 | /// | |
127 | /// ``` | |
128 | /// let five = 5; | |
129 | /// | |
3b2f2976 | 130 | /// assert_eq!(6, my_crate::add_one(5)); |
cc61c64b XL |
131 | /// ``` |
132 | pub fn add_one(x: i32) -> i32 { | |
133 | x + 1 | |
134 | } | |
3b2f2976 XL |
135 | ``` |
136 | ||
ea8adc8c | 137 | Listing 14-1: A documentation comment for a function |
cc61c64b | 138 | |
3b2f2976 XL |
139 | Here, we give a description of what the `add_one` function does, then start a |
140 | section with the heading “Examples”, and code that demonstrates how to use the | |
141 | `add_one` function. We can generate the HTML documentation from this | |
142 | documentation comment by running `cargo doc`. This command runs the `rustdoc` | |
143 | tool distributed with Rust and puts the generated HTML documentation in the | |
144 | *target/doc* directory. | |
cc61c64b | 145 | |
3b2f2976 XL |
146 | For convenience, running `cargo doc --open` will build the HTML for your |
147 | current crate’s documentation (as well as the documentation for all of your | |
148 | crate’s dependencies) and open the result in a web browser. Navigate to the | |
149 | `add_one` function and you’ll see how the text in the documentation comments | |
ea8adc8c | 150 | gets rendered, shown here in Figure 14-2: |
cc61c64b | 151 | |
3b2f2976 XL |
152 | <img alt="Rendered HTML documentation for the `add_one` function of `my_crate`" src="img/trpl14-03.png" class="center" /> |
153 | ||
ea8adc8c | 154 | Figure 14-2: HTML documentation for the `add_one` function |
3b2f2976 XL |
155 | |
156 | #### Commonly Used Sections | |
157 | ||
ea8adc8c | 158 | We used the `# Examples` markdown heading in Listing 14-1 to create a section |
3b2f2976 XL |
159 | in the HTML with the title “Examples”. Some other sections that crate authors |
160 | commonly use in their documentation include: | |
161 | ||
ea8adc8c XL |
162 | * **Panics**: The scenarios in which this function could `panic!`. Callers of |
163 | this function who don’t want their programs to panic should make sure that | |
164 | they don’t call this function in these situations. | |
165 | * **Errors**: If this function returns a `Result`, describing the kinds of | |
166 | errors that might occur and what conditions might cause those errors to be | |
167 | returned can be helpful to callers so that they can write code to handle the | |
168 | different kinds of errors in different ways. | |
169 | * **Safety**: If this function is `unsafe` to call (we will discuss unsafety in | |
170 | Chapter 19), there should be a section explaining why the function is unsafe | |
171 | and covering the invariants that this function expects callers to uphold. | |
3b2f2976 XL |
172 | |
173 | Most documentation comment sections don’t need all of these sections, but this | |
174 | is a good list to check to remind you of the kinds of things that people | |
175 | calling your code will be interested in knowing about. | |
176 | ||
177 | #### Documentation Comments as Tests | |
cc61c64b XL |
178 | |
179 | Adding examples in code blocks in your documentation comments is a way to | |
3b2f2976 XL |
180 | clearly demonstrate how to use your library, but it has an additional bonus: |
181 | running `cargo test` will run the code examples in your documentation as tests! | |
182 | Nothing is better than documentation with examples. Nothing is worse than | |
183 | examples that don’t actually work because the code has changed since the | |
cc61c64b | 184 | documentation has been written. Try running `cargo test` with the documentation |
ea8adc8c | 185 | for the `add_one` function like in Listing 14-1; you should see a section in |
3b2f2976 | 186 | the test results like this: |
cc61c64b | 187 | |
3b2f2976 XL |
188 | ``` |
189 | Doc-tests my_crate | |
cc61c64b XL |
190 | |
191 | running 1 test | |
3b2f2976 | 192 | test src/lib.rs - add_one (line 5) ... ok |
cc61c64b XL |
193 | |
194 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured | |
195 | ``` | |
196 | ||
3b2f2976 XL |
197 | Now try changing either the function or the example so that the `assert_eq!` in |
198 | the example will panic. Run `cargo test` again, and you’ll see that the doc | |
199 | tests catch that the example and the code are out of sync from one another! | |
cc61c64b | 200 | |
3b2f2976 XL |
201 | #### Commenting Contained Items |
202 | ||
3b2f2976 XL |
203 | There’s another style of doc comment, `//!`, that adds documentation to the |
204 | item that contains the comments, rather than adding documentation to the items | |
205 | following the comments. These are typically used inside the crate root file | |
ea8adc8c XL |
206 | (*src/lib.rs* by convention) or inside a module to document the crate or the |
207 | module as a whole. | |
3b2f2976 XL |
208 | |
209 | For example, if we wanted to add documentation that described the purpose of | |
210 | the `my_crate` crate that contains the `add_one` function, we can add | |
211 | documentation comments that start with `//!` to the beginning of *src/lib.rs* | |
ea8adc8c | 212 | as shown in Listing 14-3: |
3b2f2976 XL |
213 | |
214 | Filename: src/lib.rs | |
cc61c64b XL |
215 | |
216 | ``` | |
3b2f2976 | 217 | //! # My Crate |
cc61c64b | 218 | //! |
3b2f2976 XL |
219 | //! `my_crate` is a collection of utilities to make performing certain |
220 | //! calculations more convenient. | |
221 | ||
222 | /// Adds one to the number given. | |
223 | // ...snip... | |
cc61c64b XL |
224 | ``` |
225 | ||
ea8adc8c | 226 | Listing 14-3: Documentation for the `my_crate` crate as a whole |
3b2f2976 XL |
227 | |
228 | Notice there isn’t any code after the last line that begins with `//!`. Because | |
229 | we started the comments with `//!` instead of `///`, we’re documenting the item | |
230 | that contains this comment rather than an item that follows this comment. In | |
231 | this case, the item that contains this comment is the *src/lib.rs* file, which | |
232 | is the crate root. These comments describe the entire crate. | |
233 | ||
234 | If we run `cargo doc --open`, we’ll see these comments displayed on the front | |
235 | page of the documentation for `my_crate` above the list of public items in the | |
ea8adc8c | 236 | crate, as shown in Figure 14-4: |
3b2f2976 XL |
237 | |
238 | <img alt="Rendered HTML documentation with a comment for the crate as a whole" src="img/trpl14-05.png" class="center" /> | |
239 | ||
ea8adc8c | 240 | Figure 14-4: Rendered documentation for `my_crate` including the comment |
3b2f2976 XL |
241 | describing the crate as a whole |
242 | ||
3b2f2976 XL |
243 | Documentation comments within items are useful for describing crates and |
244 | modules especially. Use them to talk about the purpose of the container overall | |
245 | to help users of your crate understand your organization. | |
246 | ||
ea8adc8c | 247 | #### Exporting a Convenient Public API with `pub use` |
cc61c64b XL |
248 | |
249 | In Chapter 7, we covered how to organize our code into modules with the `mod` | |
250 | keyword, how to make items public with the `pub` keyword, and how to bring | |
3b2f2976 XL |
251 | items into a scope with the `use` keyword. The structure that makes sense to |
252 | you while you’re developing a crate may not be very convenient for your users, | |
253 | however. You may wish to organize your structs in a hierarchy containing | |
254 | multiple levels, but people that want to use a type you’ve defined deep in the | |
255 | hierarchy might have trouble finding out that those types exist. They might | |
256 | also be annoyed at having to type `use | |
257 | my_crate::some_module::another_module::UsefulType;` rather than `use | |
258 | my_crate::UsefulType;`. | |
259 | ||
3b2f2976 XL |
260 | The structure of your public API is a major consideration when publishing a |
261 | crate. People who use your crate are less familiar with the structure than you | |
262 | are, and might have trouble finding the pieces they want to use if the module | |
263 | hierarchy is large. | |
264 | ||
265 | The good news is that, if the structure *isn’t* convenient for others to use | |
266 | from another library, you don’t have to rearrange your internal organization: | |
267 | you can choose to re-export items to make a public structure that’s different | |
268 | to your private structure, using `pub use`. Re-exporting takes a public item in | |
269 | one location and makes it public in another location as if it was defined in | |
270 | the other location instead. | |
271 | ||
3b2f2976 XL |
272 | For example, say we made a library named `art` for modeling artistic concepts. |
273 | Within this library is a `kinds` module containing two enums named | |
274 | `PrimaryColor` and `SecondaryColor` and a `utils` module containing a function | |
ea8adc8c | 275 | named `mix` as shown in Listing 14-5: |
3b2f2976 XL |
276 | |
277 | Filename: src/lib.rs | |
278 | ||
279 | ``` | |
cc61c64b XL |
280 | //! # Art |
281 | //! | |
282 | //! A library for modeling artistic concepts. | |
283 | ||
284 | pub mod kinds { | |
285 | /// The primary colors according to the RYB color model. | |
286 | pub enum PrimaryColor { | |
287 | Red, | |
288 | Yellow, | |
289 | Blue, | |
290 | } | |
291 | ||
292 | /// The secondary colors according to the RYB color model. | |
293 | pub enum SecondaryColor { | |
294 | Orange, | |
295 | Green, | |
296 | Purple, | |
297 | } | |
298 | } | |
299 | ||
300 | pub mod utils { | |
301 | use kinds::*; | |
302 | ||
303 | /// Combines two primary colors in equal amounts to create | |
304 | /// a secondary color. | |
305 | pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor { | |
306 | // ...snip... | |
307 | } | |
308 | } | |
309 | ``` | |
310 | ||
ea8adc8c | 311 | Listing 14-5: An `art` library with items organized into `kinds` and `utils` |
cc61c64b XL |
312 | modules |
313 | ||
3b2f2976 | 314 | The front page of the documentation for this crate generated by `cargo doc` |
ea8adc8c | 315 | would look like Figure 14-6: |
3b2f2976 XL |
316 | |
317 | <img alt="Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules" src="img/trpl14-07.png" class="center" /> | |
318 | ||
ea8adc8c XL |
319 | Figure 14-6: Front page of the documentation for `art` that lists the `kinds` |
320 | and `utils` modules | |
cc61c64b | 321 | |
3b2f2976 XL |
322 | Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on the |
323 | front page, nor is the `mix` function. We have to click on `kinds` and `utils` | |
324 | in order to see them. | |
cc61c64b | 325 | |
3b2f2976 XL |
326 | Another crate depending on this library would need `use` statements that import |
327 | the items from `art` including specifying the module structure that’s currently | |
ea8adc8c | 328 | defined. Listing 14-7 shows an example of a crate that uses the `PrimaryColor` |
3b2f2976 | 329 | and `mix` items from the `art` crate: |
cc61c64b | 330 | |
3b2f2976 XL |
331 | Filename: src/main.rs |
332 | ||
333 | ``` | |
cc61c64b XL |
334 | extern crate art; |
335 | ||
336 | use art::kinds::PrimaryColor; | |
337 | use art::utils::mix; | |
338 | ||
339 | fn main() { | |
340 | let red = PrimaryColor::Red; | |
341 | let yellow = PrimaryColor::Yellow; | |
342 | mix(red, yellow); | |
343 | } | |
344 | ``` | |
345 | ||
ea8adc8c | 346 | Listing 14-7: A crate using the `art` crate’s items with its internal structure |
3b2f2976 | 347 | exported |
cc61c64b | 348 | |
ea8adc8c | 349 | The author of the code in Listing 14-7 that uses the `art` crate had to figure |
3b2f2976 XL |
350 | out that `PrimaryColor` is in the `kinds` module and `mix` is in the `utils` |
351 | module. The module structure of the `art` crate is more relevant to developers | |
352 | working on the `art` crate than developers using the `art` crate. The internal | |
353 | structure that organizes parts of the crate into the `kinds` module and the | |
354 | `utils` module doesn’t add any useful information to someone trying to | |
355 | understand how to use the `art` crate. The `art` crate’s module structure adds | |
356 | confusion in having to figure out where to look and inconvenience in having to | |
357 | specify the module names in the `use` statements. | |
cc61c64b | 358 | |
3b2f2976 | 359 | To remove the internal organization from the public API, we can take the `art` |
ea8adc8c XL |
360 | crate code from Listing 14-5 and add `pub use` statements to re-export the |
361 | items at the top level, as shown in Listing 14-8: | |
cc61c64b | 362 | |
3b2f2976 | 363 | Filename: src/lib.rs |
cc61c64b | 364 | |
3b2f2976 | 365 | ``` |
cc61c64b XL |
366 | //! # Art |
367 | //! | |
368 | //! A library for modeling artistic concepts. | |
369 | ||
370 | pub use kinds::PrimaryColor; | |
371 | pub use kinds::SecondaryColor; | |
372 | pub use utils::mix; | |
373 | ||
374 | pub mod kinds { | |
375 | // ...snip... | |
3b2f2976 XL |
376 | } |
377 | ||
378 | pub mod utils { | |
379 | // ...snip... | |
380 | } | |
cc61c64b XL |
381 | ``` |
382 | ||
ea8adc8c | 383 | Listing 14-8: Adding `pub use` statements to re-export items |
cc61c64b | 384 | |
3b2f2976 | 385 | The API documentation generated with `cargo doc` for this crate will now list |
ea8adc8c | 386 | and link re-exports on the front page as shown in Figure 14-9, which makes |
3b2f2976 | 387 | these types easier to find. |
cc61c64b | 388 | |
3b2f2976 XL |
389 | <img alt="Rendered documentation for the `art` crate with the re-exports on the front page" src="img/trpl14-10.png" class="center" /> |
390 | ||
ea8adc8c | 391 | Figure 14-9: Front page of the documentation for `art` that lists the |
3b2f2976 | 392 | re-exports |
cc61c64b | 393 | |
3b2f2976 | 394 | Users of the `art` crate can still see and choose to use the internal structure |
ea8adc8c XL |
395 | as in Listing 14-7, or they can use the more convenient structure from Listing |
396 | 14-8, as shown in Listing 14-10: | |
cc61c64b | 397 | |
3b2f2976 | 398 | Filename: src/main.rs |
cc61c64b | 399 | |
3b2f2976 | 400 | ``` |
cc61c64b XL |
401 | extern crate art; |
402 | ||
403 | use art::PrimaryColor; | |
404 | use art::mix; | |
405 | ||
406 | fn main() { | |
407 | // ...snip... | |
408 | } | |
409 | ``` | |
410 | ||
ea8adc8c | 411 | Listing 14-10: A program using the re-exported items from the `art` crate |
cc61c64b | 412 | |
3b2f2976 XL |
413 | In cases where there are many nested modules, re-exporting the types at the top |
414 | level with `pub use` can make a big difference in the experience of people who | |
415 | use the crate. | |
416 | ||
417 | Creating a useful public API structure is more of an art than a science, and | |
418 | you can iterate to find the API that works best for your users. Choosing `pub | |
419 | use` gives you flexibility in how you structure your crate internally, and | |
420 | decouples that internal structure with what you present to your users. Take a | |
421 | look at some of the code of crates you’ve installed to see if their internal | |
422 | structure differs from their public API. | |
423 | ||
424 | ### Setting up a Crates.io Account | |
425 | ||
426 | Before you can publish any crates, you need to create an account on crates.io | |
427 | and get an API token. To do so, visit the home page at *https://crates.io* and | |
ea8adc8c | 428 | log in via a GitHub account—the GitHub account is a requirement for now, but |
3b2f2976 XL |
429 | the site may support other ways of creating an account in the future. Once |
430 | you’re logged in, visit your account settings at *https://crates.io/me* and | |
431 | retrieve your API key. Then run the `cargo login` command with your API key, | |
432 | like this: | |
cc61c64b | 433 | |
3b2f2976 | 434 | ``` |
cc61c64b XL |
435 | $ cargo login abcdefghijklmnopqrstuvwxyz012345 |
436 | ``` | |
437 | ||
438 | This command will inform Cargo of your API token and store it locally in | |
ea8adc8c | 439 | *~/.cargo/credentials*. Note that this token is a *secret* and should not be |
3b2f2976 XL |
440 | shared with anyone else. If it is shared with anyone for any reason, you should |
441 | revoke it and generate a new token on Crates.io. | |
cc61c64b XL |
442 | |
443 | ### Before Publishing a New Crate | |
444 | ||
3b2f2976 XL |
445 | Now you have an account, and let’s say you already have a crate you want to |
446 | publish. Before publishing, you’ll need to add some metadata to your crate by | |
447 | adding it to the `[package]` section of the crate’s *Cargo.toml*. | |
448 | ||
3b2f2976 XL |
449 | Your crate will first need a unique name. While you’re working on a crate |
450 | locally, you may name a crate whatever you’d like. However, crate names on | |
451 | Crates.io are allocated on a first-come-first-serve basis. Once a crate name is | |
452 | taken, no one else may publish a crate with that name. Search for the name | |
453 | you’d like to use on the site to find out if it has been taken. If it hasn’t, | |
454 | edit the name in *Cargo.toml* under `[package]` to have the name you want to | |
455 | use for publishing like so: | |
456 | ||
ea8adc8c XL |
457 | Filename: Cargo.toml |
458 | ||
3b2f2976 XL |
459 | ``` |
460 | [package] | |
461 | name = "guessing_game" | |
462 | ``` | |
cc61c64b | 463 | |
3b2f2976 XL |
464 | Even if you’ve chosen a unique name, if you try to run `cargo publish` to |
465 | publish the crate at this point, you’ll get a warning and then an error: | |
cc61c64b | 466 | |
3b2f2976 | 467 | ``` |
cc61c64b XL |
468 | $ cargo publish |
469 | Updating registry `https://github.com/rust-lang/crates.io-index` | |
470 | warning: manifest has no description, license, license-file, documentation, | |
471 | homepage or repository. | |
472 | ...snip... | |
473 | error: api errors: missing or empty metadata fields: description, license. | |
cc61c64b XL |
474 | ``` |
475 | ||
3b2f2976 XL |
476 | This is because we’re missing some crucial information: a description and |
477 | license are required so that people will know what your crate does and under | |
478 | what terms they may use it. To rectify this error, we need to include this | |
479 | information in *Cargo.toml*. | |
cc61c64b | 480 | |
3b2f2976 XL |
481 | Make a description that’s just a sentence or two, as it will appear with your |
482 | crate in search results and on your crate’s page. For the `license` field, you | |
483 | need to give a *license identifier value*. The Linux Foundation’s Software | |
484 | Package Data Exchange (SPDX) at *http://spdx.org/licenses/* lists the | |
485 | identifiers you can use for this value. For example, to specify that you’ve | |
486 | licensed your crate using the MIT License, add the `MIT` identifier: | |
487 | ||
ea8adc8c XL |
488 | Filename: Cargo.toml |
489 | ||
3b2f2976 XL |
490 | ``` |
491 | [package] | |
492 | name = "guessing_game" | |
493 | license = "MIT" | |
494 | ``` | |
495 | ||
3b2f2976 XL |
496 | If you want to use a license that doesn’t appear in the SPDX, you need to place |
497 | the text of that license in a file, include the file in your project, then use | |
498 | `license-file` to specify the name of that file instead of using the `license` | |
499 | key. | |
cc61c64b XL |
500 | |
501 | Guidance on which license is right for your project is out of scope for this | |
502 | book. Many people in the Rust community choose to license their projects in the | |
ea8adc8c | 503 | same way as Rust itself, with a dual license of `MIT/Apache-2.0`—this |
3b2f2976 XL |
504 | demonstrates that you can also specify multiple license identifiers separated |
505 | by a slash. | |
cc61c64b | 506 | |
3b2f2976 XL |
507 | So, with a unique name, the version, and author details that `cargo new` added |
508 | when you created the crate, your description, and the license you chose added, | |
509 | the *Cargo.toml* for a project that’s ready to publish might look like this: | |
cc61c64b | 510 | |
ea8adc8c XL |
511 | Filename: Cargo.toml |
512 | ||
3b2f2976 | 513 | ``` |
cc61c64b XL |
514 | [package] |
515 | name = "guessing_game" | |
516 | version = "0.1.0" | |
517 | authors = ["Your Name <you@example.com>"] | |
518 | description = "A fun game where you guess what number the computer has chosen." | |
519 | license = "MIT/Apache-2.0" | |
520 | ||
521 | [dependencies] | |
522 | ``` | |
523 | ||
ea8adc8c | 524 | Cargo’s documentation at *https://doc.rust-lang.org/cargo/* describes other |
3b2f2976 XL |
525 | metadata you can specify to ensure your crate can be discovered and used more |
526 | easily! | |
cc61c64b XL |
527 | |
528 | ### Publishing to Crates.io | |
529 | ||
3b2f2976 XL |
530 | Now that you’ve created an account, saved your API token, chosen a name for |
531 | your crate, and specified the required metadata, you’re ready to publish! | |
532 | Publishing a crate uploads a specific version to crates.io for others to use. | |
cc61c64b | 533 | |
3b2f2976 XL |
534 | Take care when publishing a crate, because a publish is *permanent*. The |
535 | version can never be overwritten, and the code cannot be deleted. One major | |
536 | goal of Crates.io is to act as a permanent archive of code so that builds of | |
537 | all projects that depend on crates from Crates.io will continue to work. | |
538 | Allowing deletion of versions would make fulfilling that goal impossible. | |
539 | However, there is no limit to the number of versions of a crate you can publish. | |
cc61c64b | 540 | |
3b2f2976 | 541 | Let’s run the `cargo publish` command again. It should succeed now: |
cc61c64b | 542 | |
3b2f2976 | 543 | ``` |
cc61c64b XL |
544 | $ cargo publish |
545 | Updating registry `https://github.com/rust-lang/crates.io-index` | |
546 | Packaging guessing_game v0.1.0 (file:///projects/guessing_game) | |
547 | Verifying guessing_game v0.1.0 (file:///projects/guessing_game) | |
548 | Compiling guessing_game v0.1.0 | |
549 | (file:///projects/guessing_game/target/package/guessing_game-0.1.0) | |
3b2f2976 | 550 | Finished dev [unoptimized + debuginfo] target(s) in 0.19 secs |
cc61c64b XL |
551 | Uploading guessing_game v0.1.0 (file:///projects/guessing_game) |
552 | ``` | |
553 | ||
3b2f2976 XL |
554 | Congratulations! You’ve now shared your code with the Rust community, and |
555 | anyone can easily add your crate as a dependency of their project. | |
cc61c64b XL |
556 | |
557 | ### Publishing a New Version of an Existing Crate | |
558 | ||
3b2f2976 XL |
559 | When you’ve made changes to your crate and are ready to release a new version, |
560 | you change the `version` value specified in your *Cargo.toml* and republish. | |
ea8adc8c XL |
561 | Use the Semantic Versioning rules at *http://semver.org/* to decide what an |
562 | appropriate next version number is based on the kinds of changes you’ve made. | |
563 | Then run `cargo publish` to upload the new version. | |
cc61c64b XL |
564 | |
565 | ### Removing Versions from Crates.io with `cargo yank` | |
566 | ||
3b2f2976 XL |
567 | While you can’t remove previous versions of a crate, you can prevent any future |
568 | projects from adding them as a new dependency. This is useful when a version of | |
569 | a crate ends up being broken for one reason or another. For situations such as | |
570 | this, Cargo supports *yanking* a version of a crate. | |
571 | ||
ea8adc8c XL |
572 | Yanking a version prevents new projects from starting to depend on that version |
573 | while allowing all existing projects that depend on it to continue to download | |
574 | and depend on that version. Essentially, a yank means that all projects with a | |
575 | *Cargo.lock* will not break, while any future *Cargo.lock* files generated will | |
576 | not use the yanked version. | |
cc61c64b XL |
577 | |
578 | To yank a version of a crate, run `cargo yank` and specify which version you | |
579 | want to yank: | |
580 | ||
3b2f2976 | 581 | ``` |
cc61c64b XL |
582 | $ cargo yank --vers 1.0.1 |
583 | ``` | |
584 | ||
585 | You can also undo a yank, and allow projects to start depending on a version | |
586 | again, by adding `--undo` to the command: | |
587 | ||
3b2f2976 | 588 | ``` |
cc61c64b XL |
589 | $ cargo yank --vers 1.0.1 --undo |
590 | ``` | |
591 | ||
3b2f2976 XL |
592 | A yank *does not* delete any code. The yank feature is not intended for |
593 | deleting accidentally uploaded secrets, for example. If that happens, you must | |
594 | reset those secrets immediately. | |
595 | ||
cc61c64b XL |
596 | ## Cargo Workspaces |
597 | ||
598 | In Chapter 12, we built a package that included both a binary crate and a | |
3b2f2976 XL |
599 | library crate. You may find, as your project develops, that the library crate |
600 | continues to get bigger and you want to split your package up further into | |
601 | multiple library crates. In this situation, Cargo has a feature called | |
602 | *workspaces* that can help manage multiple related packages that are developed | |
603 | in tandem. | |
cc61c64b XL |
604 | |
605 | A *workspace* is a set of packages that will all share the same *Cargo.lock* | |
3b2f2976 XL |
606 | and output directory. Let’s make a project using a workspace, using trivial |
607 | code so we can concentrate on the structure of a workspace. We’ll have a binary | |
608 | that uses two libraries: one library that will provide an `add_one` function | |
609 | and a second library that will provide an `add_two` function. These three | |
610 | crates will all be part of the same workspace. We’ll start by creating a new | |
611 | crate for the binary: | |
cc61c64b | 612 | |
3b2f2976 | 613 | ``` |
cc61c64b XL |
614 | $ cargo new --bin adder |
615 | Created binary (application) `adder` project | |
616 | $ cd adder | |
617 | ``` | |
618 | ||
3b2f2976 XL |
619 | We need to modify the binary package’s *Cargo.toml* and add a `[workspace]` |
620 | section to tell Cargo the `adder` package is a workspace. Add this at the | |
621 | bottom of the file: | |
cc61c64b | 622 | |
ea8adc8c XL |
623 | Filename: Cargo.toml |
624 | ||
3b2f2976 | 625 | ``` |
cc61c64b XL |
626 | [workspace] |
627 | ``` | |
628 | ||
629 | Like many Cargo features, workspaces support convention over configuration: we | |
3b2f2976 XL |
630 | don’t need to add anything more than this to *Cargo.toml* to define our |
631 | workspace as long as we follow the convention. | |
632 | ||
3b2f2976 XL |
633 | ### Specifying Workspace Dependencies |
634 | ||
ea8adc8c XL |
635 | By default, Cargo will include all transitive path dependencies. A *path |
636 | dependency* is when any crate, whether in a workspace or not, specifies that it | |
637 | has a dependency on a crate in a local directory by using the `path` attribute | |
638 | on the dependency specification in *Cargo.toml*. If a crate has the | |
639 | `[workspace]` key, or if the crate is itself part of a workspace, and we | |
640 | specify path dependencies where the paths are subdirectories of the crate’s | |
641 | directory, those dependent crates will be considered part of the workspace. | |
642 | Let’s specify in the *Cargo.toml* for the top-level `adder` crate that it will | |
643 | have a dependency on an `add-one` crate that will be in the `add-one` | |
644 | subdirectory, by changing *Cargo.toml* to look like this: | |
3b2f2976 | 645 | |
ea8adc8c | 646 | Filename: Cargo.toml |
cc61c64b | 647 | |
3b2f2976 | 648 | ``` |
cc61c64b XL |
649 | [dependencies] |
650 | add-one = { path = "add-one" } | |
651 | ``` | |
652 | ||
3b2f2976 XL |
653 | If we add dependencies to *Cargo.toml* that don’t have a `path` specified, |
654 | those dependencies will be normal dependencies that aren’t in this workspace | |
655 | and are assumed to come from Crates.io. | |
cc61c64b | 656 | |
3b2f2976 | 657 | ### Creating the Second Crate in the Workspace |
cc61c64b | 658 | |
3b2f2976 XL |
659 | Next, while in the `adder` directory, generate an `add-one` crate: |
660 | ||
661 | ``` | |
cc61c64b XL |
662 | $ cargo new add-one |
663 | Created library `add-one` project | |
664 | ``` | |
665 | ||
666 | Your `adder` directory should now have these directories and files: | |
667 | ||
3b2f2976 | 668 | ``` |
cc61c64b XL |
669 | ├── Cargo.toml |
670 | ├── add-one | |
671 | │ ├── Cargo.toml | |
672 | │ └── src | |
673 | │ └── lib.rs | |
674 | └── src | |
675 | └── main.rs | |
676 | ``` | |
677 | ||
3b2f2976 | 678 | In *add-one/src/lib.rs*, let’s add an `add_one` function: |
cc61c64b | 679 | |
3b2f2976 | 680 | Filename: add-one/src/lib.rs |
cc61c64b | 681 | |
3b2f2976 | 682 | ``` |
cc61c64b XL |
683 | pub fn add_one(x: i32) -> i32 { |
684 | x + 1 | |
685 | } | |
686 | ``` | |
687 | ||
3b2f2976 XL |
688 | Open up *src/main.rs* for `adder` and add an `extern crate` line at the top of |
689 | the file to bring the new `add-one` library crate into scope. Then change the | |
ea8adc8c XL |
690 | `main` function to call the `add_one` function, as in Listing 14-11: |
691 | ||
692 | Filename: src/main.rs | |
cc61c64b | 693 | |
3b2f2976 | 694 | ``` |
cc61c64b XL |
695 | extern crate add_one; |
696 | ||
697 | fn main() { | |
698 | let num = 10; | |
699 | println!("Hello, world! {} plus one is {}!", num, add_one::add_one(num)); | |
700 | } | |
701 | ``` | |
702 | ||
ea8adc8c | 703 | Listing 14-11: Using the `add-one` library crate from the `adder` crate |
cc61c64b | 704 | |
3b2f2976 XL |
705 | Let’s build the `adder` crate by running `cargo build` in the *adder* directory! |
706 | ||
707 | ``` | |
cc61c64b XL |
708 | $ cargo build |
709 | Compiling add-one v0.1.0 (file:///projects/adder/add-one) | |
710 | Compiling adder v0.1.0 (file:///projects/adder) | |
3b2f2976 | 711 | Finished dev [unoptimized + debuginfo] target(s) in 0.68 secs |
cc61c64b XL |
712 | ``` |
713 | ||
3b2f2976 XL |
714 | Note that this builds both the `adder` crate and the `add-one` crate in |
715 | *adder/add-one*. Now your *adder* directory should have these files: | |
cc61c64b | 716 | |
3b2f2976 XL |
717 | ``` |
718 | ├── Cargo.lock | |
719 | ├── Cargo.toml | |
720 | ├── add-one | |
721 | │ ├── Cargo.toml | |
722 | │ └── src | |
723 | │ └── lib.rs | |
724 | ├── src | |
725 | │ └── main.rs | |
726 | └── target | |
727 | ``` | |
cc61c64b | 728 | |
3b2f2976 XL |
729 | The workspace has one *target* directory at the top level; *add-one* doesn’t |
730 | have its own *target* directory. Even if we go into the `add-one` directory and | |
731 | run `cargo build`, the compiled artifacts end up in *adder/target* rather than | |
732 | *adder/add-one/target*. The crates in a workspace depend on each other. If each | |
733 | crate had its own *target* directory, each crate in the workspace would have to | |
734 | recompile each other crate in the workspace in order to have the artifacts in | |
735 | its own *target* directory. By sharing one *target* directory, the crates in | |
736 | the workspace can avoid rebuilding the other crates in the workspace more than | |
737 | necessary. | |
738 | ||
3b2f2976 XL |
739 | #### Depending on an External Crate in a Workspace |
740 | ||
741 | Also notice the workspace only has one *Cargo.lock*, rather than having a | |
742 | top-level *Cargo.lock* and *add-one/Cargo.lock*. This ensures that all crates | |
743 | are using the same version of all dependencies. If we add the `rand` crate to | |
744 | both *Cargo.toml* and *add-one/Cargo.toml*, Cargo will resolve both of those to | |
745 | one version of `rand` and record that in the one *Cargo.lock*. Making all | |
746 | crates in the workspace use the same dependencies means the crates in the | |
747 | workspace will always be compatible with each other. Let’s try this out now. | |
748 | ||
749 | Let’s add the `rand` crate to the `[dependencies]` section in | |
750 | *add-one/Cargo.toml* in order to be able to use the `rand` crate in the | |
751 | `add-one` crate: | |
752 | ||
753 | Filename: add-one/Cargo.toml | |
cc61c64b | 754 | |
3b2f2976 | 755 | ``` |
cc61c64b XL |
756 | [dependencies] |
757 | ||
758 | rand = "0.3.14" | |
759 | ``` | |
760 | ||
3b2f2976 XL |
761 | We can now add `extern crate rand;` to *add-one/src/lib.rs*, and building the |
762 | whole workspace by running `cargo build` in the *adder* directory will bring in | |
763 | and compile the `rand` crate: | |
cc61c64b | 764 | |
3b2f2976 | 765 | ``` |
cc61c64b XL |
766 | $ cargo build |
767 | Updating registry `https://github.com/rust-lang/crates.io-index` | |
768 | Downloading rand v0.3.14 | |
769 | ...snip... | |
770 | Compiling rand v0.3.14 | |
771 | Compiling add-one v0.1.0 (file:///projects/adder/add-one) | |
772 | Compiling adder v0.1.0 (file:///projects/adder) | |
3b2f2976 | 773 | Finished dev [unoptimized + debuginfo] target(s) in 10.18 secs |
cc61c64b XL |
774 | ``` |
775 | ||
3b2f2976 XL |
776 | The top level *Cargo.lock* now contains information about `add-one`’s |
777 | dependency on `rand`. However, even though `rand` is used somewhere in the | |
778 | workspace, we can’t use it in other crates in the workspace unless we add | |
cc61c64b | 779 | `rand` to their *Cargo.toml* as well. If we add `extern crate rand;` to |
3b2f2976 | 780 | *src/main.rs* for the top level `adder` crate, for example, we’ll get an error: |
cc61c64b | 781 | |
3b2f2976 | 782 | ``` |
cc61c64b XL |
783 | $ cargo build |
784 | Compiling adder v0.1.0 (file:///projects/adder) | |
785 | error[E0463]: can't find crate for `rand` | |
786 | --> src/main.rs:1:1 | |
787 | | | |
788 | 1 | extern crate rand; | |
789 | | ^^^^^^^^^^^^^^^^^^^ can't find crate | |
790 | ``` | |
791 | ||
3b2f2976 XL |
792 | To fix this, edit *Cargo.toml* for the top level `adder` crate and indicate |
793 | that `rand` is a dependency for that crate as well. Building the `adder` crate | |
794 | will add `rand` to the list of dependencies for `adder` in *Cargo.lock*, but no | |
795 | additional copies of `rand` will be downloaded. Cargo has ensured for us that | |
796 | any crate in the workspace using the `rand` crate will be using the same | |
797 | version. Using the same version of `rand` across the workspace saves space | |
798 | since we won’t have multiple copies and ensures that the crates in the | |
799 | workspace will be compatible with each other. | |
800 | ||
801 | #### Adding a Test to a Workspace | |
cc61c64b | 802 | |
3b2f2976 XL |
803 | For another enhancement, let’s add a test of the `add_one::add_one` function |
804 | within the `add_one` crate: | |
cc61c64b | 805 | |
3b2f2976 | 806 | Filename: add-one/src/lib.rs |
cc61c64b | 807 | |
3b2f2976 | 808 | ``` |
cc61c64b XL |
809 | pub fn add_one(x: i32) -> i32 { |
810 | x + 1 | |
811 | } | |
812 | ||
813 | #[cfg(test)] | |
814 | mod tests { | |
815 | use super::*; | |
816 | ||
817 | #[test] | |
818 | fn it_works() { | |
819 | assert_eq!(3, add_one(2)); | |
820 | } | |
821 | } | |
822 | ``` | |
823 | ||
824 | Now run `cargo test` in the top-level *adder* directory: | |
825 | ||
3b2f2976 | 826 | ``` |
cc61c64b XL |
827 | $ cargo test |
828 | Compiling adder v0.1.0 (file:///projects/adder) | |
3b2f2976 | 829 | Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs |
cc61c64b XL |
830 | Running target/debug/adder-f0253159197f7841 |
831 | ||
832 | running 0 tests | |
833 | ||
834 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured | |
835 | ``` | |
836 | ||
837 | Wait a second, zero tests? We just added one! If we look at the output, we can | |
3b2f2976 XL |
838 | see that `cargo test` in a workspace only runs tests for the top level crate. |
839 | To run tests for all of the crates in the workspace, we need to pass the | |
840 | `--all` flag: | |
cc61c64b | 841 | |
3b2f2976 XL |
842 | ``` |
843 | $ cargo test --all | |
844 | Finished dev [unoptimized + debuginfo] target(s) in 0.37 secs | |
cc61c64b XL |
845 | Running target/debug/deps/add_one-abcabcabc |
846 | ||
847 | running 1 test | |
848 | test tests::it_works ... ok | |
849 | ||
3b2f2976 XL |
850 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out |
851 | ||
852 | Running target/debug/deps/adder-abcabcabc | |
853 | ||
854 | running 0 tests | |
855 | ||
856 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out | |
cc61c64b XL |
857 | |
858 | Doc-tests add-one | |
859 | ||
860 | running 0 tests | |
861 | ||
3b2f2976 XL |
862 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out |
863 | ``` | |
864 | ||
865 | When passing `--all`, `cargo test` will run the tests for all of the crates in | |
866 | the workspace. We can also choose to run tests for one particular crate in a | |
867 | workspace from the top level directory by using the `-p` flag and specifying | |
868 | the name of the crate we want to test: | |
869 | ||
870 | ``` | |
871 | $ cargo test -p add-one | |
872 | Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs | |
873 | Running target/debug/deps/add_one-b3235fea9a156f74 | |
874 | ||
875 | running 1 test | |
876 | test tests::it_works ... ok | |
877 | ||
878 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out | |
879 | ||
880 | Doc-tests add-one | |
881 | ||
882 | running 0 tests | |
883 | ||
884 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out | |
cc61c64b XL |
885 | ``` |
886 | ||
3b2f2976 XL |
887 | This output shows `cargo test` only ran the tests for the `add-one` crate and |
888 | didn’t run the `adder` crate tests. | |
889 | ||
890 | If you choose to publish the crates in the workspace to crates.io, each crate | |
891 | in the workspace will get published separately. The `cargo publish` command | |
892 | does not have an `--all` flag or a `-p` flag, so it is necessary to change to | |
893 | each crate’s directory and run `cargo publish` on each crate in the workspace | |
894 | in order to publish them. | |
cc61c64b | 895 | |
3b2f2976 XL |
896 | Now try adding an `add-two` crate to this workspace in a similar way as the |
897 | `add-one` crate for some more practice! | |
898 | ||
899 | As your project grows, consider using a workspace: smaller components are | |
900 | easier to understand individually than one big blob of code. Keeping the crates | |
901 | in a workspace can make coordination among them easier if they work together | |
902 | and are often changed at the same time. | |
cc61c64b XL |
903 | |
904 | ## Installing Binaries from Crates.io with `cargo install` | |
905 | ||
906 | The `cargo install` command allows you to install and use binary crates | |
3b2f2976 | 907 | locally. This isn’t intended to replace system packages; it’s meant to be a |
cc61c64b | 908 | convenient way for Rust developers to install tools that others have shared on |
3b2f2976 XL |
909 | crates.io. Only packages that have binary targets can be installed. A binary |
910 | target is the runnable program that gets created if the crate has a | |
911 | *src/main.rs* or another file specified as a binary, as opposed to a library | |
912 | target that isn’t runnable on its own but is suitable for including within | |
913 | other programs. Usually, crates have information in the *README* file about | |
914 | whether a crate is a library, has a binary target, or both. | |
915 | ||
3b2f2976 XL |
916 | All binaries from `cargo install` are put into the installation root’s *bin* |
917 | folder. If you installed Rust using *rustup.rs* and don’t have any custom | |
ea8adc8c XL |
918 | configurations, this will be `$HOME/.cargo/bin`. Ensure that directory is in |
919 | your `$PATH` to be able to run programs you’ve gotten through `cargo install`. | |
3b2f2976 XL |
920 | |
921 | For example, we mentioned in Chapter 12 that there’s a Rust implementation of | |
cc61c64b XL |
922 | the `grep` tool for searching files called `ripgrep`. If we want to install |
923 | `ripgrep`, we can run: | |
924 | ||
3b2f2976 | 925 | ``` |
cc61c64b XL |
926 | $ cargo install ripgrep |
927 | Updating registry `https://github.com/rust-lang/crates.io-index` | |
928 | Downloading ripgrep v0.3.2 | |
929 | ...snip... | |
930 | Compiling ripgrep v0.3.2 | |
931 | Finished release [optimized + debuginfo] target(s) in 97.91 secs | |
932 | Installing ~/.cargo/bin/rg | |
933 | ``` | |
934 | ||
935 | The last line of the output shows the location and the name of the installed | |
3b2f2976 XL |
936 | binary, which in the case of `ripgrep` is `rg`. As long as the installation |
937 | directory is in your `$PATH` as mentioned above, you can then run `rg --help` | |
938 | and start using a faster, rustier tool for searching files! | |
cc61c64b XL |
939 | |
940 | ## Extending Cargo with Custom Commands | |
941 | ||
3b2f2976 | 942 | Cargo is designed so you can extend it with new subcommands without having to |
cc61c64b XL |
943 | modify Cargo itself. If a binary in your `$PATH` is named `cargo-something`, |
944 | you can run it as if it were a Cargo subcommand by running `cargo something`. | |
3b2f2976 XL |
945 | Custom commands like this are also listed when you run `cargo --list`. Being |
946 | able to `cargo install` extensions and then run them just like the built-in | |
947 | Cargo tools is a super convenient benefit of Cargo’s design! | |
cc61c64b XL |
948 | |
949 | ## Summary | |
950 | ||
951 | Sharing code with Cargo and crates.io is part of what makes the Rust ecosystem | |
3b2f2976 XL |
952 | useful for many different tasks. Rust’s standard library is small and stable, |
953 | but crates are easy to share, use, and improve on a timeline different from the | |
954 | language itself. Don’t be shy about sharing code that’s useful to you on | |
955 | Crates.io; it’s likely that it will be useful to someone else as well! |