TS McNamara (23) [Avatar] Offline
#1
Manning informs me that people are actually buying the book! That means that it's time to start a placeholder topic for Chapter 2. Do feel free to ask any questions or offer advice!
Darcy Parker (1) [Avatar] Offline
#2
I am enjoying the introduction to Rust and look forward to learning more.

Minor comment about section 2.2: "floating point (f) is able to represent real numbers" would be better if it said "approximate" instead of "represent".

And related to this, there is an introduction to formatting macros for integer based types... Can you add some examples of formatting macros for floating point types?

Side note: I am curious if there is a formatting macro for printf! for outputting a float as a hexfloat formatted string?

The applications of different floating point formatting macros is beyond the scope of this book. But it is probably worth mentioning some of the formatting macros available in the language. (The reason I ask specifically about formatting hexfloats, is that it would align well with Rust's goal of being safe/reliable systems language. There not easy for humans to read.. but useful for safe/reliable data exchange.)

My understanding is that in C, it is possible to roundtrip a float back and forth between a decimal formatted string and float in memory (using scanf and printf) even though you can't represent all floats accurately in decimal formatted strings. The trick is carefully chosen conventions on how to do the rounding... but many (most) implementations are flawed. http://www.exploringbinary.com/ has some interesting articles on the topic. I am assuming Rust gets the rounding conventions right. But because many implementations of libc and other languages get it wrong... strings formatted as hexfloats are practical for data exchange because they eliminate ambiguity. With hexfloats there is no need for libraries to correctly interpret/implement special rules about outputting a value given rounding errors in order round trip from a float in memory to a string and back.
TS McNamara (23) [Avatar] Offline
#3
Minor comment about section 2.2: "floating point (f) is able to represent real numbers" would be better if it said "approximate" instead of "represent".


That's very true! I actually wrestled with this and wondered if I should mention that it's actually representing a range rather than a real number. I've ended up pushing that to chapter 5, which talks a lot about how data types are represented in the machine.

And related to this, there is an introduction to formatting macros for integer based types... Can you add some examples of formatting macros for floating point types?
Side note: I am curious if there is a formatting macro for printf! for outputting a float as a hexfloat formatted string?


Sure thing. This is also in ch5. Here is a bit of a hint:
fn main() {
    println!("{:x}", 42);
    println!("{:b}", 1024);
}


Bit manipulation is a great skill! I'm actually considering chatting about the graph6 data format, which is a way of encoding adjacency lists in a very compact manner.

If you would like, I can send you a pre-pre-release chapter 5 that still needs some editing if you're interested. Send me a pm. Thanks for the comment!
abattisti (3) [Avatar] Offline
#4
Misleading explanation for listing 2.15
Hi, I believe that in explanation for Listing 2.15 (Defining Arrays and Iterating Over Their Elements) is misleading.

let one = [1,2,3];

[1,2,3] denotes an array literal and Rust infers its type itself (probably [i32; 3])


While I believe this to be true in general, I think this is not true for this specific case. Later the variable arrays is bound to an array containing one as well as two and blank2. The later two were explicitly declared to be of type [u8; 3].

This will cause the type inference engine to infer arrays to be of the type [[u8; 3]; 4] which requires one to be of type [u8; 3].


I don't know if this is a reasonable recommendation as I am not a native speaker, but in the enumeration of the learning difficulties face by new comers is the sentence:
It’s easy to get confused with slices [T], which do not have a compile-time length.

Maybe: "..., whose length is not determined by it's type"?


Best, Alex

p.s. thanks for writing this book! smilie
267849 (17) [Avatar] Offline
#5
The description before Listing 2.4 seems to describe an older version of the code that contained

for item in haystack.iter() {
instead of the current
for reference in haystack.iter() {
  let item = *reference;


Also, the current code runs just fine, the remark in parantheses is no longer correct.
267849 (17) [Avatar] Offline
#6
Section 2.5.1:

The "In essence..." sentence is misplaced. What the text currently says is that 'a and 'b are local to the function because they say that i and j have independent lifetimes, which is exactly backwards. The overall intent of the declaration is to say that i and j have independent lifetimes, and it does so by introducing two local lifetime parameters that are assigned to the two arguments.

It might also be useful to give a motivating example to explain what lifetimes are good for, in the examples they don't do anything apart from making the code harder to read.

And all those &s appear a bit out of thin air here, I think this section should be preceeded by a section explaining the difference between by value and by reference parameters. (One might argue that "let e =&a;" is already missing in "Control" or "Size of the Language".)

Section 2.5.2:

The curt "Lastly" hides the motivation for trait bounds somwhere near the end of the paragraph, I think it would be better to start with something like "Of course we cannot add values of abritrary types, so we should ask the compiler..."


Overall, I'd merge 2.4 and 2.5, with the introductory add example, and then subsections on value and reference parameters, generic parameters and finally the forewarning about explicit lifetime annotations.
267849 (17) [Avatar] Offline
#7
The introductory sentences in section 2.6 sound a bit demotivational to me, expecially given the fact that listing 2.12 is simpler than listing 2.5. Just say that you are now starting with grep-lite.
267849 (17) [Avatar] Offline
#8
Misleading explanation for listing 2.15
abattisti wrote:
This will cause the type inference engine to infer arrays to be of the type [[u8; 3]; 4] which requires one to be of type [u8; 3].

This is indeed correct, as can be verified by changing the definition of one:
$ rustc arrays.rs 
warning: literal out of range for u8
 --> arrays.rs:2:19
  |
2 |   let one = [1,22,333];
  |                   ^^^
  |
  = note: #[warn(overflowing_literals)] on by default
jkauzlar (3) [Avatar] Offline
#9
In section 2.9, I'm very curious to know where clap accesses the command line arguments, since there isn't anything like argv and argc being passed to main(). It'd be good to give at least a brief explanation of this here.
55925 (14) [Avatar] Offline
#10
The spiral approach
The section "Beginners are welcome" states:

The chapter takes a spiral approach to learning. A few concepts will be discussed multiple times.


Such spiral approach can be a good way to step down into topics, but looking at Figure 2.1 and the contents of the chapter, the whole spiral seems to lack some sort of structured path currently.

For one, I could see that some topics (e.g. Control Flow) are spread over different sections, but some are not mentioned at all in this chapter.

So is "Complex Types" another chapter (3), and methods from "Functions and methods" are part of that other chapter too.

"Collections: vectors, arrays and tuples" unfortunately lacks the tuple part, as well as chapter 3 AFAIC.

What bothers me in general is, that some topics are only introduced implicitly in code examples and their notes.

For example "Control Flow if/else, match and looping" :

The if/else syntax is never explained explicitly but simply used in code. Match has its own section. But looping is only presented as for-in loops implicitly when talking about arrays and vectors.
Suddenly somewhere the "loop" construct appears out of thin air in a code snippet.
Are there other loop constructs? while? do-while? We do not know what we not know.

Perhaps the figure 2.1 could be a less "completely unscientific" map smilie and provide more orientation to the reader, where we are on our journey.
55925 (14) [Avatar] Offline
#11
2.1.1
1.:

Note (4) states "would change the semantics to return () rather than i32"

As we are not introduced to different special types in Rust, "return ()" can be confusing, as this concept is not known in many other languages.
The parentheses are only two symbols to the reader so far, not a named type of some kind (e.g. 'unit' or 'void' or whatever).

2.:

"Line 1" - bullet point 2 "main() .... takes no arguments ..."
That should be more prominent, in case this is the main location to explain that concept, as most languages I know indeed take arguments.

- bullet point 3 : "... defined with braces ({ & })" - here curly brackets (that's what you call it at the top line of the page) are called braces now, and the symbolic part is confusing. First, the characters should better be inside quotes, second the use of ampersand is irritating, as ampersand is an often used operator in Rust . So better would be: ( '{' and '}' )

"Line 9" - "who has worked with a typed programming [] before" <- here "language" is missing.


NB: The "compiling code with rustc" box is a nice brief explanation to bring rustc and cargo in relation!.
55925 (14) [Avatar] Offline
#12
2.2 - last paragraph
The last paragraph of 2.2 begins with: "This general pattern..." - but it is not clear what exactly the general pattern is in the above code.
55925 (14) [Avatar] Offline
#13
2.3 disambiguating 'match'
1.:

The section starts with: "Let's say that we wanted to match against multiple patterns" - which is ambiguous, depending on which languages you come from.
Pattern-matching is evolving in many modern languages, but each of them put a disclaimer on front, that this term is not about Regular Expression patterns for matching texts in strings!


2.:

Listing 2.5 : It is remarkable -and perhaps should be mentioned- that Rusts match branches are comma separated (not semicolon), and that differently to switch in other languages there is no fallthrough and thus no 'break' required.

3.:

Note (3) : "42 | 132 matches both 42 and 132" should better state "42 or 132"
55925 (14) [Avatar] Offline
#14
2.5 - better switch sub chapters ?
Reading chapter 2.5, I got the impression that the sub chapters 2.5.1 and 2.5.1 should be switched.

Generics are relatively common in programming languages today, so are somehow expected and easier to start into advanced function definitions, keeping the hard part (lifetimes) for later.

BTW: After reading the two sub chapters I wondered how a combined function signature with generics AND lifetimes would look like, as both put their parameter lists between the function name and the open parenthesis.
55925 (14) [Avatar] Offline
#15
2.5.1 lifetime parameters
1.:

Listing 2.7 - Note (1) - better say "introduces the lifetime parameters 'a and 'b ..." ?

2.:

Next paragraph "In general terms..." - "...have data that exists outside of their scope" - better "get" or "work on" than "have" ?

3.:

Last sentence

The use of references is an unfortunate necessity of introducing lifetimes, as lifetimes are not required for "normal" arguments


I struggled a while to grok what that may mean for me as a reader and Rust user, but came to the conclusion that you mean it to be an "unfortunate necessity" for you as author for constructing an example, and that typically no references would be used for an addition function. Am I right here?

Or what is this sentence about?


NB: I liked the hint about lifetime parameters and references vs. "normal" arguments in this sentence. I also appreciated the paragraph starting with "Underpinning Rust's safety..."



55925 (14) [Avatar] Offline
#16
2.5.2 generics - and general concern about "new features on the fly"
1.:

"A trait is a language feature that is analogous to an interface or protocol in other domains" - shouldn't that be "in other programming languages"?

(I consider 'domain' to be more about the field of problem we write programs for).

2.:

Listing 2.11 introduces tuples and destructuring out of a sudden, explaining it very briefly in note (2). This is a distraction here, as the section is about generics, not other features.

To be honest, I am a bit concerned about the continuous introduction of formerly unexplained features in side notes of code snippets in unrelated chapters.

As far as I could see by thumbing to the former pages, this is the first time we are in contact with tuples and destructuring.
And even Chapter 3, which is about "Compound Data Types" has no separate section about tuples.

What bothers me is, that the book may only be readable from front to end to learn Rust and that you have to carefully consider each and every foot note, to not miss an important topic.

After the full reading it will be hard to find the part where some specific fact was mentioned, as it will not be easily findable in the table of content, and creating a useful tag index may be difficult for Manning.

I say this because I know about different books where the index was only partly helpful in finding statements which I remembered reading, so typically I look into the table of content to find the section where a specific topic is discussed.


Is that concern unfounded?