rollup merge of #22954: ches/docs
Greetings Rustaceans! I've just been getting acquainted with Rust through the guide. First let me say that it's already in great shape, chapters are kept a good length to be digestible and paced to move the reader along fluidly, so my compliments to contributors! Along the way I noticed a few minor copy errors, and also a few areas that I thought more subjectively could stand to be improved. My commits here are divided so that minor edits unlikely to be very contentious could be cherry-picked, and then topically on parts that might generate more discussion. I also have some comments and questions that aren't directly associated with any changes on this branch yet. I'm not sure how you like to triage this sort of thing but I'll present them below and if it's appropriate they could be moved to separate issues or I might be able to help work some of them out within the scope of this PR. Sorry that these are a lot to take in, pretty much everything below here can be digested independently of the current changes in this PR so you could read the rest later 😄 ### Questions and Comments I'll give stable links to doc revisions as of this writing. 1. The [example using `PartialEq` in the Traits chapter][1] is poor—we have no idea how `PartialEq` works at this point in the text (or at any point, AFAICT), so it isn't clear why it won't work as a trait bound in this situation and `Float` almost magically does, with the aid of existing tailor-made identity functions that seem unlikely to be so conveniently available when we encounter a scenario like this in our real-world code. This section just seems glossed over, or perhaps content has moved around over time or there's an assumption that implementing equality with `PartialEq` should be covered in the guide eventually so this example will be less foreign. As it stands the text is hard to follow and not very meaningful. 2. I found treatment of the relationship of trait objects to pointers in the *Static and Dynamic Dispatch* chapter unclear. [The "Why Pointers?" section][2] opens with this line: > The use of language like "fat pointer" implies that a trait object is always a pointer of some form, but why? But the phrase "fat pointer" hasn't been used anywhere before. This is some of the more complex material in the guide, but this section nevertheless feels displaced, not clearly connecting preceding subject matter. Earlier we've covered the internal representation of trait objects and significance of pointers they contain, but it hasn't been spelled out (other than what `&Foo` syntax suggests) that trait objects are references (and why). That's what the "Why Pointers?" section is aiming to do I gather, but it seems out of place, I think it'd make more sense to cover this before the gory details of their internals. 3. Suggestion: move the *Error Handling* chapter much earlier in the Intermediate section of the guide, or even into the Basics section. I know the Intermediate section isn't intended to be read in order per se, but plenty of people like me are just going to read it straight through anyway 😁 These are pretty fundamental concepts to understand and `Option`, `Result`, and idioms like `unwrap()` and `.ok().expect()` are referenced numerous times throughout the rest of the guide. They feature pretty prominently as early as *Standard Input* and *Guessing Game* chapters in Basics, in fact. I happen to have a good understanding of these already through encountering their analogs in typed functional languages, but if I didn't I believe I really would have appreciated reading *Error Handling* much earlier. 4. In the `rustdoc` chapter, a [comment at the beginning of the first source example][3] refers to a "link" crate attribute being needed. There seems to be no such attribute present in the source. I believe this refers to `crate_type` [according to the reference][4], but it'd be nice if this example were updated/clarified (I think `crate_id` is deprecated/obsolete too). This brings me to a related comment also: after encountering crate attributes in the reference and also docs on Cargo configuration like `crate-type = ["dylib"]`, I'm uncertain about the relationship/redundancy between these. I'm sure this is the kind of thing where docs are simply struggling to keep pace with rapid changes in Rust and Cargo, just wanted to flag that this distinction ought to be clearly covered in the docs for one or the other at some point, it's presently hard to track down. 5. Minor: link to sample editor configurations in [the introductory chapter][5] is broken, probably the generator automatically translates `.md` links to `.html`. Perhaps it shouldn't do that for absolute URLs. 6. Following from my changes to the enums coverage in [*Compound Data Types*][6] in this PR: sum types are an important topic and I tried to make some improvements, but I think the motivating example of `Character` with `Digit(i32)` and `Other` variants is a pretty weak one, and a better example could greatly improve cohesion with the `Ordering` coverage later in the section and how that ties into pattern matching in the subsequent chapter. I just haven't thought of a better example to suggest yet. In particular, the text states: > This may seem rather limiting, but it's a limitation which we can overcome. This is referring to `Character`, and actually to more than one limitation: the preceding admonition that its variants aren't comparable/don't have ordering, and don't support binary operators like `*` and `+`. Overcoming these limitations actually never gets explained—we next cover how `Ordering` works as an enum itself for plain `i32`s, but never get around to showing how this might be applied to our `Digit` variant type. Since the coverage of enums already segues into pattern matching and this could be even tighter with a stronger example, it might be nice if our example enum were somehow connected to the final example program for the Basics section too, where `Ordering` reappears. I don't see how it would fit with the current guessing game example, but food for thought. 7. `#[derive]` seems conspicuously missing from the guide. It would probably make sense to introduce after showing simple examples of implementing equality and/or ordering traits by hand, which have been mentioned as possibilities above. Perhaps it's too much to breach this as early as the Basic section though without traits being introduced. `#[derive]` itself and the derivable traits can certainly be saved for Intermediate and referenced as covered later, in any case. r? @steveklabnik for docs. [1]:1576142495/src/doc/trpl/traits.md (our-inverse-example)
[2]:1576142495/src/doc/trpl/static-and-dynamic-dispatch.md (why-pointers)
[3]:1576142495/src/doc/trpl/documentation.md (creating-documentation)
[4]: http://doc.rust-lang.org/reference.html#linkage [5]:1576142495/src/doc/trpl/hello-world.md
[6]:1576142495/src/doc/trpl/compound-data-types.md (enums)
This commit is contained in:
commit
d6054e4771
@ -47,7 +47,7 @@ This pattern is very powerful, and we'll see it repeated more later.
|
||||
|
||||
There are also a few things you can do with a tuple as a whole, without
|
||||
destructuring. You can assign one tuple into another, if they have the same
|
||||
contained types and arity. Tuples have the same arity when they have the same
|
||||
contained types and [arity]. Tuples have the same arity when they have the same
|
||||
length.
|
||||
|
||||
```rust
|
||||
@ -196,8 +196,9 @@ Now, we have actual names, rather than positions. Good names are important,
|
||||
and with a struct, we have actual names.
|
||||
|
||||
There _is_ one case when a tuple struct is very useful, though, and that's a
|
||||
tuple struct with only one element. We call this a *newtype*, because it lets
|
||||
you create a new type that's similar to another one:
|
||||
tuple struct with only one element. We call this the *newtype* pattern, because
|
||||
it allows you to create a new type, distinct from that of its contained value
|
||||
and expressing its own semantic meaning:
|
||||
|
||||
```{rust}
|
||||
struct Inches(i32);
|
||||
@ -216,7 +217,7 @@ destructuring `let`, as we discussed previously in 'tuples.' In this case, the
|
||||
|
||||
Finally, Rust has a "sum type", an *enum*. Enums are an incredibly useful
|
||||
feature of Rust, and are used throughout the standard library. An `enum` is
|
||||
a type which ties a set of alternates to a specific name. For example, below
|
||||
a type which relates a set of alternates to a specific name. For example, below
|
||||
we define `Character` to be either a `Digit` or something else. These
|
||||
can be used via their fully scoped names: `Character::Other` (more about `::`
|
||||
below).
|
||||
@ -228,8 +229,8 @@ enum Character {
|
||||
}
|
||||
```
|
||||
|
||||
An `enum` variant can be defined as most normal types. Below are some example
|
||||
types which also would be allowed in an `enum`.
|
||||
Most normal types are allowed as the variant components of an `enum`. Here are
|
||||
some examples:
|
||||
|
||||
```rust
|
||||
struct Empty;
|
||||
@ -239,15 +240,15 @@ struct Status { Health: i32, Mana: i32, Attack: i32, Defense: i32 }
|
||||
struct HeightDatabase(Vec<i32>);
|
||||
```
|
||||
|
||||
So you see that depending on the sub-datastructure, the `enum` variant, same as
|
||||
a struct, may or may not hold data. That is, in `Character`, `Digit` is a name
|
||||
tied to an `i32` where `Other` is just a name. However, the fact that they are
|
||||
distinct makes this very useful.
|
||||
You see that, depending on its type, an `enum` variant may or may not hold data.
|
||||
In `Character`, for instance, `Digit` gives a meaningful name for an `i32`
|
||||
value, where `Other` is only a name. However, the fact that they represent
|
||||
distinct categories of `Character` is a very useful property.
|
||||
|
||||
As with structures, enums don't by default have access to operators such as
|
||||
compare ( `==` and `!=`), binary operations (`*` and `+`), and order
|
||||
(`<` and `>=`). As such, using the previous `Character` type, the
|
||||
following code is invalid:
|
||||
As with structures, the variants of an enum by default are not comparable with
|
||||
equality operators (`==`, `!=`), have no ordering (`<`, `>=`, etc.), and do not
|
||||
support other binary operations such as `*` and `+`. As such, the following code
|
||||
is invalid for the example `Character` type:
|
||||
|
||||
```{rust,ignore}
|
||||
// These assignments both succeed
|
||||
@ -265,9 +266,10 @@ let four_equals_ten = four == ten;
|
||||
```
|
||||
|
||||
This may seem rather limiting, but it's a limitation which we can overcome.
|
||||
There are two ways: by implementing equality ourselves, or by using the
|
||||
[`match`][match] keyword. We don't know enough about Rust to implement equality
|
||||
yet, but we can use the `Ordering` enum from the standard library, which does:
|
||||
There are two ways: by implementing equality ourselves, or by pattern matching
|
||||
variants with [`match`][match] expressions, which you'll learn in the next
|
||||
chapter. We don't know enough about Rust to implement equality yet, but we can
|
||||
use the `Ordering` enum from the standard library, which does:
|
||||
|
||||
```
|
||||
enum Ordering {
|
||||
@ -277,9 +279,8 @@ enum Ordering {
|
||||
}
|
||||
```
|
||||
|
||||
Because we did not define `Ordering`, we must import it (from the std
|
||||
library) with the `use` keyword. Here's an example of how `Ordering` is
|
||||
used:
|
||||
Because `Ordering` has already been defined for us, we will import it with the
|
||||
`use` keyword. Here's an example of how it is used:
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering;
|
||||
@ -313,17 +314,17 @@ the standard library if you need them.
|
||||
|
||||
Okay, let's talk about the actual code in the example. `cmp` is a function that
|
||||
compares two things, and returns an `Ordering`. We return either
|
||||
`Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on if
|
||||
the two values are less, greater, or equal. Note that each variant of the
|
||||
`enum` is namespaced under the `enum` itself: it's `Ordering::Greater` not
|
||||
`Greater`.
|
||||
`Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on
|
||||
whether the first value is less than, greater than, or equal to the second. Note
|
||||
that each variant of the `enum` is namespaced under the `enum` itself: it's
|
||||
`Ordering::Greater`, not `Greater`.
|
||||
|
||||
The `ordering` variable has the type `Ordering`, and so contains one of the
|
||||
three values. We then do a bunch of `if`/`else` comparisons to check which
|
||||
one it is.
|
||||
|
||||
This `Ordering::Greater` notation is too long. Let's use `use` to import the
|
||||
`enum` variants instead. This will avoid full scoping:
|
||||
This `Ordering::Greater` notation is too long. Let's use another form of `use`
|
||||
to import the `enum` variants instead. This will avoid full scoping:
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering::{self, Equal, Less, Greater};
|
||||
@ -347,16 +348,18 @@ fn main() {
|
||||
```
|
||||
|
||||
Importing variants is convenient and compact, but can also cause name conflicts,
|
||||
so do this with caution. It's considered good style to rarely import variants
|
||||
for this reason.
|
||||
so do this with caution. For this reason, it's normally considered better style
|
||||
to `use` an enum rather than its variants directly.
|
||||
|
||||
As you can see, `enum`s are quite a powerful tool for data representation, and are
|
||||
even more useful when they're [generic][generics] across types. Before we
|
||||
get to generics, though, let's talk about how to use them with pattern matching, a
|
||||
tool that will let us deconstruct this sum type (the type theory term for enums)
|
||||
in a very elegant way and avoid all these messy `if`/`else`s.
|
||||
As you can see, `enum`s are quite a powerful tool for data representation, and
|
||||
are even more useful when they're [generic][generics] across types. Before we
|
||||
get to generics, though, let's talk about how to use enums with pattern
|
||||
matching, a tool that will let us deconstruct sum types (the type theory term
|
||||
for enums) like `Ordering` in a very elegant way that avoids all these messy
|
||||
and brittle `if`/`else`s.
|
||||
|
||||
|
||||
[arity]: ./glossary.html#arity
|
||||
[match]: ./match.html
|
||||
[game]: ./guessing-game.html#comparing-guesses
|
||||
[generics]: ./generics.html
|
||||
|
@ -40,14 +40,14 @@ us enforce that it can't leave the current thread.
|
||||
|
||||
### `Sync`
|
||||
|
||||
The second of these two trait is called [`Sync`](../std/marker/trait.Sync.html).
|
||||
The second of these traits is called [`Sync`](../std/marker/trait.Sync.html).
|
||||
When a type `T` implements `Sync`, it indicates to the compiler that something
|
||||
of this type has no possibility of introducing memory unsafety when used from
|
||||
multiple threads concurrently.
|
||||
|
||||
For example, sharing immutable data with an atomic reference count is
|
||||
threadsafe. Rust provides a type like this, `Arc<T>`, and it implements `Sync`,
|
||||
so that it could be safely shared between threads.
|
||||
so it is safe to share between threads.
|
||||
|
||||
These two traits allow you to use the type system to make strong guarantees
|
||||
about the properties of your code under concurrency. Before we demonstrate
|
||||
@ -69,7 +69,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
The `Thread::scoped()` method accepts a closure, which is executed in a new
|
||||
The `thread::scoped()` method accepts a closure, which is executed in a new
|
||||
thread. It's called `scoped` because this thread returns a join guard:
|
||||
|
||||
```
|
||||
@ -208,10 +208,10 @@ Here's the error:
|
||||
|
||||
```text
|
||||
<anon>:11:9: 11:22 error: the trait `core::marker::Send` is not implemented for the type `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` [E0277]
|
||||
<anon>:11 Thread::spawn(move || {
|
||||
<anon>:11 thread::spawn(move || {
|
||||
^~~~~~~~~~~~~
|
||||
<anon>:11:9: 11:22 note: `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` cannot be sent between threads safely
|
||||
<anon>:11 Thread::spawn(move || {
|
||||
<anon>:11 thread::spawn(move || {
|
||||
^~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
@ -322,7 +322,6 @@ While this channel is just sending a generic signal, we can send any data that
|
||||
is `Send` over the channel!
|
||||
|
||||
```
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::sync::mpsc;
|
||||
|
||||
|
@ -430,7 +430,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
But it is not idiomatic. This is significantly more likely to introducing a
|
||||
But it is not idiomatic. This is significantly more likely to introduce a
|
||||
naming conflict. In our short program, it's not a big deal, but as it grows, it
|
||||
becomes a problem. If we have conflicting names, Rust will give a compilation
|
||||
error. For example, if we made the `japanese` functions public, and tried to do
|
||||
|
@ -115,8 +115,9 @@ doesn't work, so we're okay with that. In most cases, we would want to handle
|
||||
the error case explicitly. `expect()` allows us to give an error message if
|
||||
this crash happens.
|
||||
|
||||
We will cover the exact details of how all of this works later in the Guide.
|
||||
For now, this gives you enough of a basic understanding to work with.
|
||||
We will cover the exact details of how all of this works later in the Guide in
|
||||
[Error Handling]. For now, this gives you enough of a basic understanding to
|
||||
work with.
|
||||
|
||||
Back to the code we were working on! Here's a refresher:
|
||||
|
||||
@ -157,3 +158,6 @@ here.
|
||||
|
||||
That's all you need to get basic input from the standard input! It's not too
|
||||
complicated, but there are a number of small parts.
|
||||
|
||||
|
||||
[Error Handling]: ./error-handling.html
|
||||
|
Loading…
x
Reference in New Issue
Block a user