Skip to content

Commit

Permalink
Merge main into unsafe-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
chriskrycho committed Oct 16, 2024
2 parents e63e487 + 1cbfc5c commit 19f8321
Show file tree
Hide file tree
Showing 42 changed files with 587 additions and 449 deletions.
5 changes: 5 additions & 0 deletions ADMIN_TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ occasional maintenance tasks.
To update the `edition = "[year]"` metadata in all the listings' `Cargo.toml`s,
run the `./tools/update-editions.sh` script and commit the changes.

## Update the `edition` in mdBook config

Open `book.toml` and `nostarch/book.toml` and set the `edition` value in the
`[rust]` table to the new edition.

## Release a new version of the listings

We now make `.tar` files of complete projects containing every listing
Expand Down
22 changes: 21 additions & 1 deletion book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,33 @@

[book]
title = "The Rust Programming Language"
authors = ["Steve Klabnik", "Carol Nichols", "Contributions from the Rust Community"]
authors = ["Steve Klabnik", "Carol Nichols", "Chris Krycho", "Contributions from the Rust Community"]

[output.html]
additional-css = ["ferris.css", "theme/2018-edition.css", "theme/semantic-notes.css", "theme/listing.css"]
additional-js = ["ferris.js"]
git-repository-url = "https://github.com/rust-lang/book"

[output.html.redirect]
"ch17-00-oop.html" = "ch18-00-oop.html"
"ch17-01-what-is-oo.html" = "ch18-01-what-is-oo.html"
"ch17-02-trait-objects.html" = "ch18-02-trait-objects.html"
"ch17-03-oo-design-patterns.html" = "ch18-03-oo-design-patterns.html"
"ch18-00-patterns.html" = "ch19-00-patterns.html"
"ch18-01-all-the-places-for-patterns.html" = "ch19-01-all-the-places-for-patterns.html"
"ch18-02-refutability.html" = "ch19-02-refutability.html"
"ch18-03-pattern-syntax.html" = "ch19-03-pattern-syntax.html"
"ch19-00-advanced-features.html" = "ch20-00-advanced-features.html"
"ch19-01-unsafe-rust.html" = "ch20-01-unsafe-rust.html"
"ch19-03-advanced-traits.html" = "ch20-03-advanced-traits.html"
"ch19-04-advanced-types.html" = "ch20-04-advanced-types.html"
"ch19-05-advanced-functions-and-closures.html" = "ch20-05-advanced-functions-and-closures.html"
"ch19-06-macros.html" = "ch20-06-macros.html"
"ch20-00-final-project-a-web-server.html" = "ch21-00-final-project-a-web-server.html"
"ch20-01-single-threaded.html" = "ch21-01-single-threaded.html"
"ch20-02-multithreaded.html" = "ch21-02-multithreaded.html"
"ch20-03-graceful-shutdown-and-cleanup.html" = "ch21-03-graceful-shutdown-and-cleanup.html"

# Do not sync this preprocessor; it is for the HTML renderer only.
[preprocessor.trpl-note]

Expand Down
1 change: 1 addition & 0 deletions ci/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ JoinHandle
Kay's
kinded
Klabnik
Krycho
lang
LastWriteTime
latin
Expand Down
3 changes: 3 additions & 0 deletions nostarch/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ build-dir = "../tmp"

[preprocessor.trpl-listing]
output-mode = "simple"

[rust]
edition = "2021"
69 changes: 43 additions & 26 deletions nostarch/chapter17.md
Original file line number Diff line number Diff line change
Expand Up @@ -1669,10 +1669,10 @@ The version with `yield_now` is *way* faster!
This means that async can be useful even for compute-bound tasks, depending on
what else your program is doing, because it provides a useful tool for
structuring the relationships between different parts of the program. This is a
form of *cooperative multitasking*, where each future has both the power to
determine when it hands over control via await points. Each future therefore
also has the responsibility to avoid blocking for too long. In some Rust-based
embedded operating systems, this is the *only* kind of multitasking!
form of *cooperative multitasking*, where each future has the power to determine
when it hands over control via await points. Each future therefore also has the
responsibility to avoid blocking for too long. In some Rust-based embedded
operating systems, this is the *only* kind of multitasking!

In real-world code, you won’t usually be alternating function calls with await
points on every single line, of course. While yielding control in this way is
Expand Down Expand Up @@ -2046,7 +2046,7 @@ indicates a message arrived in time; the `Err` variant indicates that the
timeout elapsed before any message arrived. We `match` on that result and either
print the message when we receive it successfully, or print a notice about the
timeout. Finally, notice that we pin the messages after applying the timeout to
them, because the timeout helper produces a future which needs to be pinned to
them, because the timeout helper produces a stream which needs to be pinned to
be polled.

Filename: src/main.rs
Expand Down Expand Up @@ -2507,7 +2507,7 @@ it is not yet ready.

### Pinning and the Pin and Unpin Traits

When we introduced the idea of pinning, while working on Listing 17-17, we ran
When we introduced the idea of pinning while working on Listing 17-17, we ran
into a very gnarly error message. Here is the relevant part of it again:

```
Expand Down Expand Up @@ -2535,12 +2535,20 @@ For more information about an error, try `rustc --explain E0277`.

When we read this error message carefully, it not only tells us that we need to
pin the values, but also tells us why pinning is required. The `trpl::join_all`
function returns a struct called `JoinAll`. That struct, in turn, is generic
over a type `F`, which is constrained to implement the `Future` trait. Finally,
directly awaiting a Future requires that the future in question implement the
`Unpin` trait. That’s a lot! But we can understand it, if we dive a little
further into how the `Future` type actually works, in particular around
*pinning*.
function returns a struct called `JoinAll`. That struct is generic over a type
`F`, which is constrained to implement the `Future` trait. Directly awaiting a
future with `await` pins the future implicitly. That’s why we don’t need to use
`pin!` everywhere we want to await futures.

However, we’re not directly awaiting a future here. Instead, we construct a new
future, `JoinAll`, by passing a collection of futures to the `join_all`
function. The signature for `join_all` produces requires that the type of the
items in the collection all implement the `Future` trait, and `Box<T>` only
implements `Future` if the `T` that it wraps is a future which implements the
`Unpin` trait.

That’s a lot! But we can understand it, if we dive a little further into how the
`Future` type actually works, in particular around *pinning*.

Let’s look again at the definition of `Future`:

Expand Down Expand Up @@ -2684,24 +2692,32 @@ that a given type does *not* need to uphold any particular guarantees about
whether the value in question can be moved.

Just as with `Send` and `Sync`, the compiler implements `Unpin` automatically
for all types where it can prove it is safe. Implementing `Unpin` manually is
unsafe because it requires *you* to uphold all the guarantees which make `Pin`
and `Unpin` safe yourself for a type with internal references. In practice,
this is a very rare thing to implement yourself!
for all types where it can prove it is safe. The special case, again similar to
`Send` and `Sync`, is the case where `Unpin` is *not* implemented for a type.
The notation for this is `impl !Unpin for SomeType`, where `SomeType` is the
name of a type which *does* need to uphold those guarantees to be safe whenever
a pointer to that type it is used in a `Pin`.

In other words, there are two things to keep in mind about the relationship
between `Pin` and `Unpin`. First, `Unpin` is the “normal” case, and `!Unpin` is
the special case. Second, whether a type implements `Unpin` or `!Unpin` *only*
matters when using a pinned pointer to that type like `Pin<&mut SomeType>`.

To make that concrete, think about a `String`: it has a length and the Unicode
characters which make it up. We can wrap a `String` in `Pin`, as seen in Figure
17-7. However
17-8. However, `String` automatically implements `Unpin`, the same as most other
types in Rust.

<img alt="Concurrent work flow" src="img/trpl17-08.svg" />

Figure 17-8: Pinning a String, with a dotted line indicating that the String
implements the `Unpin` trait, so it is not pinned.

This means that we can do things such as replace one string with another at the
exact same location in memory as in Figure 17-9. This doesn’t violate the `Pin`
contract because `String`—like most other types in Rust—implements `Unpin`,
because it has no internal references that make it unsafe to move around!
As a result, we can do things which would be illegal if `String` implemented
`!Unpin` instead, such as replace one string with another at the exact same
location in memory as in Figure 17-9. This doesn’t violate the `Pin` contract,
because `String` has no internal references that make it unsafe to move around!
That is precisely why it implements `Unpin` rather than `!Unpin`.

<img alt="Concurrent work flow" src="img/trpl17-09.svg" />

Expand All @@ -2710,9 +2726,10 @@ Figure 17-9: Replacing the String with an entirely different String in memory.
Now we know enough to understand the errors reported for that `join_all` call
from back in Listing 17-17. We originally tried to move the futures produced by
async blocks into a `Vec<Box<dyn Future<Output = ()>>>`, but as we’ve seen,
those futures may have internal references, so they don’t implement `Unpin`.
They need to be pinned, and then we can pass the `Pin` type into the `Vec`,
confident that the underlying data in the futures will *not* be moved.
those futures may have internal references, so they don’t automatically
implement `Unpin`. Once we pin them, we can pass the resulting `Pin` type into
the `Vec`, confident that the underlying data in the futures will *not* be
moved.

`Pin` and `Unpin` are mostly important for building lower-level libraries, or
when you’re building a runtime itself, rather than for day to day Rust code.
Expand Down Expand Up @@ -2943,9 +2960,9 @@ threads *and* tasks, and therefore futures.
As a default way of thinking about which to use when:
* If the task is *very parallelizable*, such as processing a bunch of data where
* If the work is *very parallelizable*, such as processing a bunch of data where
each part can be processed separately, threads are a better choice.
* If the task is *very concurrent*, such as handling messages from a bunch of
* If the work is *very concurrent*, such as handling messages from a bunch of
different sources which may come in a different intervals or different rates,
async is a better choice.
Expand Down
21 changes: 11 additions & 10 deletions packages/mdbook-trpl-listing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ impl<'e> ListingState<'e> {
) -> Result<(), String> {
// We do not *keep* the version constructed here, just temporarily
// construct it so the HTML parser, which expects properly closed tags
// to parse it as a *tag* rather than a *weird text node*, which accept
// to parse it as a *tag* rather than a *weird text node*, will accept
// it and provide a useful view of it.
let to_parse = tag.to_owned().to_string() + "</Listing>";
let listing = Dom::parse(&to_parse)
Expand All @@ -212,21 +212,22 @@ impl<'e> ListingState<'e> {
.try_fold(ListingBuilder::new(), |builder, (key, maybe_value)| {
match (key.as_str(), maybe_value) {
("number", Some(value)) => Ok(builder.with_number(value)),
("number", None) => {
Err(String::from("number attribute without value"))
}

("caption", Some(value)) => Ok(builder.with_caption(value)),
("caption", None) => {
Err(String::from("caption attribute without value"))
}

("file-name", Some(value)) => {
Ok(builder.with_file_name(value))
}
("file-name", None) => {
Err(String::from("file-name attribute without value"))

(attr @ "file-name", None)
| (attr @ "caption", None)
| (attr @ "number", None) => {
Err(format!("Missing value for attribute: '{attr}'"))
}

_ => Ok(builder), // TODO: error on extra attrs?
(attr, _) => {
Err(format!("Unsupported attribute name: '{attr}'"))
}
}
})?
.build();
Expand Down
103 changes: 103 additions & 0 deletions packages/mdbook-trpl-listing/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,5 +188,108 @@ fn main() {}
);
}

#[test]
fn with_unsupported_attr_name() {
let result = rewrite_listing(
"<Listing invalid-attr>
```rust
fn main() {}
```
</Listing>",
Mode::Default,
);

assert_eq!(
result,
Err(String::from("Unsupported attribute name: 'invalid-attr'"))
)
}

#[test]
fn with_unsupported_attr_name_with_arg() {
let result = rewrite_listing(
r#"<Listing invalid-attr="123">
```rust
fn main() {}
```
</Listing>"#,
Mode::Default,
);

assert_eq!(
result,
Err(String::from("Unsupported attribute name: 'invalid-attr'"))
)
}

#[cfg(test)]
mod missing_value {
use super::*;

#[test]
fn for_number() {
let result = rewrite_listing(
r#"<Listing number>
```rust
fn main() {}
```
</Listing>"#,
Mode::Default,
);

assert_eq!(
result,
Err(String::from("Missing value for attribute: 'number'"))
)
}

#[test]
fn for_caption() {
let result = rewrite_listing(
r#"<Listing caption>
```rust
fn main() {}
```
</Listing>"#,
Mode::Default,
);

assert_eq!(
result,
Err(String::from("Missing value for attribute: 'caption'"))
)
}

#[test]
fn for_file_name() {
let result = rewrite_listing(
r#"<Listing file-name>
```rust
fn main() {}
```
</Listing>"#,
Mode::Default,
);

assert_eq!(
result,
Err(String::from("Missing value for attribute: 'file-name'"))
)
}
}

#[test]
fn missing_value() {}

#[cfg(test)]
mod config;
2 changes: 1 addition & 1 deletion src/appendix-02-operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ hierarchy to an item.
| Symbol | Explanation |
|--------|-------------|
| `ident::ident` | Namespace path |
| `::path` | Path relative to the crate root (i.e., an explicitly absolute path) |
| `::path` | Path relative to the extern prelude, where all other crates are rooted (i.e., an explicitly absolute path including crate name) |
| `self::path` | Path relative to the current module (i.e., an explicitly relative path). |
| `super::path` | Path relative to the parent of the current module |
| `type::ident`, `<type as trait>::ident` | Associated constants, functions, and types |
Expand Down
10 changes: 6 additions & 4 deletions src/ch06-01-defining-an-enum.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ only know what *kind* it is. Given that you just learned about structs in
Chapter 5, you might be tempted to tackle this problem with structs as shown in
Listing 6-1.

<Listing number="6-1" caption="Storing the data and `IpAddrKind` variant of an IP address using a `struct`">

```rust
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-01/src/main.rs:here}}
```

<span class="caption">Listing 6-1: Storing the data and `IpAddrKind` variant of
an IP address using a `struct`</span>
</Listing>

Here, we’ve defined a struct `IpAddr` that has two fields: a `kind` field that
is of type `IpAddrKind` (the enum we defined previously) and an `address` field
Expand Down Expand Up @@ -140,12 +141,13 @@ more about bringing types into scope in Chapter 7.
Let’s look at another example of an enum in Listing 6-2: this one has a wide
variety of types embedded in its variants.

<Listing number="6-2" caption="A `Message` enum whose variants each store different amounts and types of values">

```rust
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-02/src/main.rs:here}}
```

<span class="caption">Listing 6-2: A `Message` enum whose variants each store
different amounts and types of values</span>
</Listing>

This enum has four variants with different types:

Expand Down
Loading

0 comments on commit 19f8321

Please sign in to comment.