Skip to content

Commit

Permalink
Rollup merge of rust-lang#22954 - ches:docs, r=steveklabnik
Browse files Browse the repository at this point in the history
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]: https://github.com/rust-lang/rust/blob/157614249594f187f421cd97f928e64c5ab5c1fa/src/doc/trpl/traits.md#our-inverse-example
[2]: https://github.com/rust-lang/rust/blob/157614249594f187f421cd97f928e64c5ab5c1fa/src/doc/trpl/static-and-dynamic-dispatch.md#why-pointers
[3]: https://github.com/rust-lang/rust/blob/157614249594f187f421cd97f928e64c5ab5c1fa/src/doc/trpl/documentation.md#creating-documentation
[4]: http://doc.rust-lang.org/reference.html#linkage
[5]: https://github.com/rust-lang/rust/blob/157614249594f187f421cd97f928e64c5ab5c1fa/src/doc/trpl/hello-world.md
[6]: https://github.com/rust-lang/rust/blob/157614249594f187f421cd97f928e64c5ab5c1fa/src/doc/trpl/compound-data-types.md#enums
  • Loading branch information
steveklabnik committed Mar 23, 2015
2 parents 809a554 + 92294e7 commit 4a81e66
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 42 deletions.
69 changes: 36 additions & 33 deletions src/doc/trpl/compound-data-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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).
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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};
Expand All @@ -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
11 changes: 5 additions & 6 deletions src/doc/trpl/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:

```
Expand Down Expand Up @@ -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 || {
^~~~~~~~~~~~~
```

Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/doc/trpl/crates-and-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions src/doc/trpl/standard-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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

0 comments on commit 4a81e66

Please sign in to comment.