Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion on section 3: Primitive Rust #9

Closed
steveklabnik opened this issue Dec 16, 2015 · 38 comments
Closed

Discussion on section 3: Primitive Rust #9

steveklabnik opened this issue Dec 16, 2015 · 38 comments

Comments

@steveklabnik
Copy link
Member

No description provided.

@billpmurphy
Copy link

Unless I'm missing something obvious, there's no chapter or section on pattern matching and Rust's pattern syntax! You briefly introduce pattern matching in 3.1, and show an example of a match in chapter 11, but the match keyword and the syntax are never formally introduced. Does it make sense to add this to 3.1, or as its own subsection of chapter 3?

@steveklabnik
Copy link
Member Author

It's going to end up in the section on match. Remember, everything after The Basics (currently) is just whatever the text in the old chapter was; it's likely incoherent and doesn't necessarily represent what the chapter will end up as once I take a look.

Good eye though!

On Dec 19, 2015, 00:51 -0500, [email protected], wrote:

Unless I'm missing something obvious, there's no chapter or section on pattern matching and Rust's pattern syntax! You briefly introduce pattern matching in 3.1, and show an example of amatchin chapter 11, but the match keyword and the syntax are never formally introduced. Does it make sense to add this to 3.1, or as its own subsection of chapter 3?


Reply to this email directly orview it on GitHub(#9 (comment)).

@aturon
Copy link
Member

aturon commented Dec 21, 2015

I'll post notes on each subsection as I review them. Here's the first.

The basics

We should work on the title. We need something more narrow than "The
basics", maybe one of:

  • Functions, variables, and control flow
  • Basic declarations and control flow
  • Primitive Rust

We’ll start our journey with Rust by talking about the absolute
basics. Many programming languages have lots in common at their
core.

How about: We’ll start our journey with Rust by talking about the absolute
basics -- concepts that appear in almost every programming language.

Much of this may feel similar to other languages you’ve used, but
some parts are different.

Maybe say: none of the concepts presented are unique to Rust, but
we'll cover Rust's particular syntax and conventions around these
common concepts.

Variable bindings

Good opener.

The basics of bindings

I think you want let PATTERN = EXPRESSION; -- a value is an
expression that is already done evaluating, like a constant.

I'd start more narrowly and then forward-reference the generalization
to patterns. It's also important to cover some of the details that
might seem obvious. For example, you could say:

This let statement has the form let NAME = EXPRESSION;, which
first evaluates the EXPRESSION, and then binds the resulting value
to NAME so that it can be referred to later in the program. In our
simple example, the expression was already a value, 5, but we could
achieve the same effect with:

let x = 2 + 3;

In general, let statements work with patterns; a name is a
particularly humble form of pattern. We'll see more complex and
powerful patterns as we go along.

Note: println! is a macro not because of optional arguments, but
because the arguments it takes depends on the format string you give
it
.

The format string can contain the special text {}. If it does, it
fills in that place in the string with the values of the other
arguments.

Need to more clearly say: each instance of {} corresponds to an
additional argument. Probably best to give an example:

let x = 2 + 3;
let y = x + 5;
println!("The value of x is {}, and the value of y is {}", x, y);

Multiple assignment

I'd change the title from "assignment" to "binding". The word
"assignment" should be restricted to talking about mutation.

This subsection is interesting, because you can understand it as a
kind of special thing ("multiple binding"), but also in terms of
pattern matching once you understand tuples.

I think it's fine to have the section here, as is, but I think when we
discuss tuples we should revisit and explain how a tuple is a
compound value and patterns are about destructuring compound values.

Type annotations

Very good.

Initialization

Rename to: Delayed initialization.

I think it's important to mention why you'd ever want this. Just a
quick note that it's most useful if you want to initialize the value
based on the outcome of some condition, for example.

Otherwise, great.

Mutable bindings

Great! I especially loved the use of tuple patterns to show the nuance
of the syntax here.

Shadowing

So good!

@aturon
Copy link
Member

aturon commented Dec 21, 2015

More:

Functions

Might mention that snake case is used for variable names as well.

Function arguments

I think the syntax you want for arguments is PATTERN: TYPE. Remember
that type annotations are not an optional part of patterns; they are a
separate bit of syntax usable only in specific situations.

Return values

Perfect.

Statements and expressions

Rust is an ‘expression oriented’ language. This means that most
language constructs are an expression. But what are expressions?

I think this can be dropped entirely. After all, you repeat the same
basic sentiment in the next paragraph.

It's probably helpful to say that "expressions compute something;
statements bind or do something" or something along those lines.

Otherwise, great, and nice choice of examples.

Expression statements

Probably good to say that the most common case here is when calling a
function purely "for side effect". This is exactly what the opening
example for 3.2 does! So worth tying back to that.

In practice, Rust programmers don’t often think about these rules at
this level. Usually, you have a semicolon at the end of most lines,
and maybe not at the end of blocks.

You should tie back into: most blocks involve a bunch of steps of
doing/binding things, and then the computation of a final value. So,
sequence of statements, ending expression.

When looking at the rules, they can seem a bit strange, but in
practice, it works well.

Leave this out, it's not adding much :)

Multiple return values

Nice. I like the running use of tuples as a simple way to show a bit
more of the power/complexity available in the features you're talking
about.

@steveklabnik
Copy link
Member Author

I think you want let PATTERN = EXPRESSION; -- a value is an
expression that is already done evaluating, like a constant.

Ah, but we haven't talked about expressions yet. I believe that I later update this when we do.

@steveklabnik
Copy link
Member Author

👍 to basically everything else, I'm gonna try "primitive rust" to see how that feels

@steveklabnik
Copy link
Member Author

I think it's important to mention why you'd ever want this. Just a
quick note that it's most useful if you want to initialize the value
based on the outcome of some condition, for example.

We haven't talked about if yet, though....

@steveklabnik
Copy link
Member Author

Shipped all the changes except for the custom initialization.

@aturon
Copy link
Member

aturon commented Dec 21, 2015

Re: talking about EXPRESSION and conditionals -- in both cases, just make a quick forward reference. Remember we're assuming some programming knowledge coming in, so I think it's reasonable to mention "expressions" and "conditionals" in passing before we've fully defined them in Rust. I think it's even OK to use them in examples beforehand, e.g. let x = 2 + 3;.

@Keats
Copy link

Keats commented Dec 22, 2015

General

  • is it possible to have runnable code example like rustbyexample?

Variable bindings

  • it explains what is main() but this is already explained in 1.2
  • Extended error explanations
    It uses rustc rather than cargo like the rest of the examples around it, maybe switch to explain using cargo?
  • the bindings explanation (up until the scope) seems too chatty if the target is a programmer imo

Primitive types

  • chart of integer types not rendering
  • defualts -> defaults (should i do a PR for typos?)
  • arrays panic & debug text seem a bit out of place

Comments

  • why not talk about Inner documentation comments in details there?

if

  • most developers already know if/else if so the first part seems too long
  • if as an expression -> I'd mention it's the way to do ternary operations in Rust for people searching

I'm not entirely what kind of feedback you are looking for, let me know if this is ok and i'll do more chapters

@mdinger
Copy link

mdinger commented Dec 22, 2015

@Keats In a sense, the examples are runnable in the playpen when you click the link on the right however the document style isn't really setup for examples like rustbyexample's. It's very common for an example to only include the necessary new syntax whereas rustbyexample would have to include all the background info to actually make the example work. Compare this book example with this rustbyexample example.

Even if the examples were the same (and I don't think they are), the book examines each part piecemeal requiring the reader to put all the pieces together. Rustbyexample must lay it all out at once. Neither style is wrong but they are different and each lends itself to a slightly different approach. I think they are complementary and neither necessarily must be viewed as entirely better than the other.

@steveklabnik
Copy link
Member Author

@Keats thanks!

is it possible to have runnable code example like rustbyexample?

Well, the book has never had it like RustByExample, but linking to the playpen would at least stop it from being a regression. https://github.com/azerupi/mdBook/issues/29 is tracking this generally.

it explains what is main() but this is already explained in 1.2

This is true, but 1) repetition is okay, it's a part of learning and 2) I expect a lot of people to skip that section, so it's repeated here. That said, a callback to that might be nice.

Extended error explanations It uses rustc rather than cargo like the rest of the examples around it, maybe switch to explain using cargo?

The issue is that cargo doesn't support --explain, it's a rustc only thing. This might be worth calling out though, or explaining in more depth.

the bindings explanation (up until the scope) seems too chatty if the target is a programmer imo

This is just a style issue. A lot of people love it, some people hate it. It's how I write, though, and we've generally taken that docs will have a conversational tone.

chart of integer types not rendering

Thanks, would you mind filing a separate bug for this? I'll look into it.

defualts -> defaults (should i do a PR for typos?)

Yes please! I thought this one was already fixed by another PR.

arrays panic & debug text seem a bit out of place

It is not ideal, but we can't really skip it. If you have any direct suggestions, that'd be great, it's certainly something that I don't feel spectacular about.

why not talk about Inner documentation comments in details there?

Because the only real use of inner doc comments is to document modules, and we haven't talked about them yet.

most developers already know if/else if so the first part seems too long

I am willing to err on the side of too much explaining, generally speaking.

if as an expression -> I'd mention it's the way to do ternary operations in Rust for people searching

In general, we try not to refer to features Rust doesn't have, as this gets really endless. This is a book about writing Rust, not necessarily "how to write your fave feature from $LANGUAGE" in Rust.

I'm not entirely what kind of feedback you are looking for, let me know if this is ok and i'll do more chapters

This is generally great! Some of it just comes down to things I haven't written down, exactly, or choices we've made, but I'd rather have too much feedback than not enough.

I would say that any chapter tagged S-rough-draft is fair game. Not a whole lot of that yet :)

@Keats
Copy link

Keats commented Dec 22, 2015

For the examples I agree that it's not too suitable to some examples or would require rewriting quite a bit, I just found all the "Create a cargo project.." a bit distracting.

I was sure Cargo had a way of explaining an error, I must have dreamt it.

I'm reading the book hosted on github so i guess that depends how often you update it, I will do PRs/continue tomorrow

@leoyvens
Copy link
Contributor

I find "The Basics" more welcoming than "Primitive Rust", 'primitive' may even sound pejorative.

In "Variable Bindings", I found "Reassignment, not mutation" part very confusing, perhaps because it's lacking the rest of the explanation in the "Functions" section. Still I think it should be removed and the difference should be explained when it matters.

@steveklabnik
Copy link
Member Author

'primitive' may even sound pejorative.

I had wondered about this, but wasn't sure.

@aturon aturon changed the title Discussion on section 3: The Basics Discussion on section 3: Primitive Rust Dec 28, 2015
@aturon
Copy link
Member

aturon commented Dec 28, 2015

Here's some feedback on Primitive Types, leaving out functions and arrays for now:

Primitive Types

I'm not sure that the most important split here is between built-in
types and standard library types -- that is, I think we should instead
focus on "basic" types, all of which will happen to be built in:

  • Scalars: ints, floats, chars, bools
  • Compound types: tuples, arrays

I'd suggest breaking the "primitive types" subsections into two -- one
for scalars, and one for compound types.

I would leave &str and fn types to later sections -- I suggest the
String and closures sections, respectively. (The latter would be an
advanced section, and the title might need to be expanded to "closures
and function types" or some such.) Note that slices &[T] are also
built-in types; I'd suggest pairing them with the section on vectors.

Why move those out?

  • For &str, you haven't seen & yet, and there's not a lot yet to
    say about the type. We really want to cover borrowing and owned
    strings before this discussion can be useful, I think. That said, if
    you want to keep the current discussion paired with char, just say
    that we'll have much more to say about the &str type (including why
    you need an &) later on.
  • For the fn types, they are extremely rarely used, and are a form
    of higher-order programming. I think it's best to couple the two
    discussions together, and in any case it's a big distraction to
    discuss fn types so early.

General point: the sub-sections are inconsistently named. Some use
generic names like "Integers" or "Tuples", others use type names like
"char" and "str". I would use the generic names -- so "Characters" and
"String slices".

Integers

  • "Unsigned numbers are always positive" => never negative
  • two's complement
  • Probably worth mentioning that the usual operations apply, and
    giving some syntax: +, -, *, / etc

Floating point

Seems fine to not spend a lot of time on this. Same thing about syntax
for operations, though.

Tuples

I think you want a slightly longer intro here, because you haven't
really explained this type previously -- all the previous mentions
have not treated it like a type, but rather as a special way to do
multiple binding/return.

In fact, I'd start out by saying exactly that:

We've seen tuples before, in the guise of binding or returning
multiple values at once. It turns out that there's no magic here:
tuples are a general way of making a compound value that groups some
number of other values with distinct types. The number of values
grouped is the arity of the tuple.

We create a tuple by writing a comma-separated list of values inside
parentheses; each position in the tuple has a distinct type:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

Note that, unlike the examples of multiple bindings, here we bound the
single name tup to the entire tuple. We can then use pattern
matching to destructure this tuple value:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("y is {}", y);
}

Tuple indexing

With the above suggested text, you can now say: "In addition to
destructuring through pattern matching, we can also access a tuple
element directly using . followed by the index we want to access:

Functions

Let's come back to this, after deciding where it should live.

Booleans

Rather than "That's really all ... " -- say something like: "The main
way to consume boolean values is through conditionals like if, which
we'll see later in the chapter."

Char

This is fine, but as with all the other sections it'd be good to list
at least a couple of the common ways of doing something with a
char. We may need to think on that a bit.

str

I've already given you my basic feeling here. I think it's fine to say
that the string literals we've been writing have a type &str that we
won't be able to explain fully until we have more concepts under our belt.

@aturon
Copy link
Member

aturon commented Dec 28, 2015

re: "Primitive Rust", I agree that the title isn't ideal. But I think "The Basics" is problematic, because the chapter definitely doesn't cover the basics of Rust.

@steveklabnik
Copy link
Member Author

This seems good. I am torn about str; I agree with your points, but we've also worked with str a lot, and probably will.... hrm.

@aturon
Copy link
Member

aturon commented Dec 28, 2015

@steveklabnik Yeah -- I think something like what you have now is probably fine, if bolstered with a more explicit forward-ref to the material that's ahead.

@aturon
Copy link
Member

aturon commented Dec 28, 2015

Arrays

"So far, we’ve only represented single values in a binding." This is
actually not true -- the tuple section gives examples of compound
values. And really, you want to explain arrays and tuples in contrast
to each other. E.g., both contain some number of values, but arrays
require all the values to have the same type.

We need to say that in these examples, the arrays are allocated
directly on the stack; we'll show later how to do heap allocation.

I'd shorten the panic section somewhat -- drop the mention of the
macro, and shorten the explanation to a forward reference:

For now, all you need to know is that a panic will crash your
program. Rust's error handling story is described in full in a later
chapter.

The Debug section I think is good to include here, as people are
bound to try printing things like arrays. I would say that Display
is "intended for direct end-user consumption", whereas Debug is
"intended for programmer consumption".

@azerupi
Copy link
Contributor

azerupi commented Dec 28, 2015

It feels weird to introduce panic and Debug in between primitive types. I understand you have to introduce them somewhere in the beginning, but they seem out of place..

This is going to confuse a lot of people, like it is introduced here people might think panic an debug are types (I hesitated when I read it).

@aturon
Copy link
Member

aturon commented Dec 28, 2015

@azerupi In the printed version of the book I'm expecting these would be clearly marked sidebars -- I wonder if we can do something similar for the online version?

I do agree that these are a digression from the main text, but including them early is countering another source of confusion -- trying to index out of bounds or print an array, and getting surprising results.

If we can't do an explicit sidebar here, maybe we can find some other placement early on?

@steveklabnik
Copy link
Member Author

We can get some sidebar stuff with CSS, at least... I'm just really bad at it.

@azerupi
Copy link
Contributor

azerupi commented Dec 28, 2015

If we can't do an explicit sidebar here, maybe we can find some other placement early on?

You can't add classes to the markdown directly (limitation of the markdown parser) but you can insert raw html. So you can basically add whatever you want.

I totally agree that those concepts should be introduced early on (at least briefly). But they should probably not be associated with types at all. Maybe you could add a sub-chapter, to the primitive types chapter, called "simple debugging" that would explain printing to the console, format and debug, panics, ...?

@asolove
Copy link

asolove commented Dec 29, 2015

If you always preface the sidebar section headings with something predictable ("Sidebar:"?) we can dynamically add in a div with a class to style it however.

@steveklabnik
Copy link
Member Author

called "simple debugging" that would explain printing to the console, format and debug, panics, ...?

I like this idea! Hmm

@aturon
Copy link
Member

aturon commented Dec 29, 2015

The comments section looks good, but I think we should drop the inner doc comments bit and only introduce it when we talk about modules.

@aturon
Copy link
Member

aturon commented Dec 29, 2015

if

Overall, solid, quick read.

As I said in a different bit of feedback, I think we should consistently title
sections with a full name, rather than just using keywords like if. Perhaps
"Control flow with if" or something?

I like the opening, but I think it ends up quite belabored. I'd suggest dropping
the paragraph immediately after the quote. I think the text still makes perfect
sense that way, and the allusion the quote is more subtle.

if as an expression

"This also means that you almost certainly need an else when using if in this
way" -- in fact, it's required.

@azerupi
Copy link
Contributor

azerupi commented Dec 29, 2015

"Control flow with if"

Or simply "if statement" ?

@steveklabnik
Copy link
Member Author

in fact, it's required.

I mean, it's not required, it's only required if you're doing anything useful. Otherwise you always get (). I wasn't sure how to word that... I guess that if it's not useful, it's basically required.

@steveklabnik
Copy link
Member Author

Or simply "if statement" ?

if isn't a statement though, it's an expression.

steveklabnik added a commit that referenced this issue Dec 29, 2015
@steveklabnik
Copy link
Member Author

I've just pushed a series of commits which take into account all the feedback before this comment 👍

Well, still unsure about 'primitive'

@aturon
Copy link
Member

aturon commented Dec 29, 2015

@steveklabnik

I mean, it's not required ...

You're right; I stand corrected!

@steveklabnik
Copy link
Member Author

I still ended up just eliminating it. It's too deep in the weeds to bother with.

@aturon
Copy link
Member

aturon commented Dec 29, 2015

I read the Loops section. It's great! I think it's ok to stick with .iter() for the time being -- you can just say that for loops work with iterators, which we're getting here via an .iter() method, and then forward-ref to the chapter on iterators.

@mdinger
Copy link

mdinger commented Dec 29, 2015

Is there any reason to cover setup via cargo if it's covered in the intro "hello world" section?

In general, let statements work with patterns; a name is a particularly humble form of pattern.

I don't know what a humble pattern means. I don't think patterns possess a state of humility or arrogance. [EDIT] I suppose it should be interpreted as simple.

The variable bindings The basics of bindings is slightly unwieldy and unfocused. Of the 6 possible subsections it covers, only 1 actually talks about bindings. All the others are basically background info:

  • you talk about cargo
  • then introduce hello world
  • then explain main
  • then start to explain bindings (what the section is about)
  • then diverge for println!
  • finally, move on to a new section: Multiple binding

This isn't a big issue since single bindings are simple but if say you separated it into a prelim and a single/multiple bindings section (or two sections back to back as it is currently), you would avoid that issue (if you consider it a problem).

@steveklabnik
Copy link
Member Author

Is there any reason to cover setup via cargo if it's covered in the intro "hello world" section?

In general, it's better to go really, really slow at the start of a book. Like, slower than you'd expect. It's accessible to a much wider audience than this way.

It's also why, at this stage, I'm still showing full examples with a main() and everything. After the basics, it'll go faster.

This isn't a big issue since single bindings are simple but if say you separated it into a prelim and a single/multiple bindings section

Hmmm yeah, this might be worthwhile.

@steveklabnik
Copy link
Member Author

I've done one or two more little tweaks; mostly removing my own home directory from the example paths :)

I'm going to send this draft to No Starch today.

jossue33 pushed a commit to jossue33/book that referenced this issue Feb 17, 2018
Spanish translation ch03-03-how-functions-work.md
tatsuya6502 referenced this issue in rust-lang-ja/book-ja Nov 21, 2019
まえがきのレビュー
amitu pushed a commit to FifthTry/rust-book that referenced this issue Jun 1, 2021
amitu pushed a commit to FifthTry/rust-book that referenced this issue Jun 1, 2021
Translating ch00-00-introduction.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants