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

A few tiny issues in chapter 2 #3794

Open
StefanSalewski opened this issue Dec 8, 2023 · 4 comments
Open

A few tiny issues in chapter 2 #3794

StefanSalewski opened this issue Dec 8, 2023 · 4 comments
Milestone

Comments

@StefanSalewski
Copy link

StefanSalewski commented Dec 8, 2023

Some weeks ago I reported already some more serious issues separately.

Still, Chapter 2 has some more minor stylistic issues which might make reading it unnecessary difficult. I will summarize these here, in the hope that these reports may be valuable for you. Note that I am still learning Rust, and I am not a native English speaker.

If a type you want to use isn’t in the prelude, you have to bring that type into scope explicitly with a use statement. Using the std::io library provides you with a number of useful features, including the ability to accept user input.

I would recommend to use the phrase "If a symbol you want to use isn’t in the prelude, you have to bring it into scope explicitly with a use statement." because import by use keyword is necessary not only for data types, but for functions and constants as well.

The fn syntax declares a new function; the parentheses, (), indicate there are no parameters; and the curly bracket, {, starts the body of the function.

My suggestion: "The fn keyword declares a new function; the empty parentheses, (), indicate there are no parameters; and the curly bracket, {, starts the body of the function."

String is a string type provided by the standard library that is a growable, UTF-8 encoded bit of text.

Suggestion: "String is a data type provided by the standard library that is a growable, UTF-8 encoded bit of text."

In full, the let mut guess = String::new(); line has created a mutable variable that is currently bound to a new, empty instance of a String. Whew!

Here the issue is "has created". Replacing with "creates" improves readability and is more correct, as that lines indeed creates the variable when we compile and run the code.

Receiving User Input

Recall that we included the input/output functionality from the standard library with use std::io; on the first line of the program. Now we’ll call the stdin function from the io module, which will allow us to handle user input:

io::stdin()
    .read_line(&mut guess)

If we hadn’t imported the io library with use std::io; at the beginning of the program, we could still use the function by writing this function call as std::io::stdin. The stdin function returns an instance of std::io::Stdin, which is a type that represents a handle to the standard input for your terminal.

Here a tiny issue is, that the second to last sentence interrupts the flow of reading. So I would suggest exchanging the last two sentences, or rewrite that section.

.expect("Failed to read line");

As others already reported, the string argument of expect() should express an expectation. This is explained in the API docs. Rust beginners would read the above code as "We do expect a failure". But the opposite is true, we expect (hope for) a valid input.

Ensuring Reproducible Builds with the Cargo.lock File

Cargo has a mechanism that ensures you can rebuild the same artifact every time you or anyone else builds your code: Cargo will use only the versions of the dependencies you specified until you indicate otherwise. For example, say that next week version 0.8.6 of the rand crate comes out, and that version contains an important bug fix, but it also contains a regression that will break your code. To handle this, Rust creates the Cargo.lock file the first time you run cargo build, so we now have this in the guessing_game directory.

When you build a project for the first time, Cargo figures out all the versions of the dependencies that fit the criteria and then writes them to the Cargo.lock file. When you build your project in the future, Cargo will see that the Cargo.lock file exists and will use the versions specified there rather than doing all the work of figuring out versions again. This lets you have a reproducible build automatically. In other words, your project will remain at 0.8.5 until you explicitly upgrade, thanks to the Cargo.lock file. Because the Cargo.lock file is important for reproducible builds, it’s often checked into source control with the rest of the code in your project.

The second sentence "Cargo will use only..." does not really fits at this position and interrupts the flow of reading. Removing it improves the text, but a complete rewrite may further improve it.

As I, as a non native speaker, are not able to really improve this text segment, I asked GPT-4 for an rewrite. See

Ensuring Reproducible Builds with the Cargo.lock File

Cargo includes a feature that guarantees the reproducibility of your builds. It ensures that the same artifact is produced every time you or anyone else compiles your code. Cargo achieves this by using the specific versions of dependencies you've defined until you decide to update them.

Consider this scenario: Suppose a new version (0.8.6) of the rand crate is released next week. This version introduces an important bug fix but also a regression that would break your code. To manage such situations, Rust generates the Cargo.lock file the first time you run cargo build. This file is created in your project's directory, such as guessing_game.

The first time you build your project, Cargo calculates the best-fitting versions of all dependencies and records them in the Cargo.lock file. For subsequent builds, Cargo refers to this file, using the specified versions, thereby avoiding the need to reevaluate the dependencies each time. This approach guarantees that your build is reproducible. For example, your project will continue to use version 0.8.5 of a dependency until you explicitly upgrade it.

Given its crucial role in ensuring consistent builds, the Cargo.lock file is typically included in source control along with your project's code. This inclusion helps maintain the consistency of builds across different environments and among different developers working on the same project.

Ultimately, we want to convert the String the program reads as input into a real number type so we can compare it numerically to the secret number.

Here, "real number" may be irritating, as it reminds to floating point numbers, because in math and Wirthian languages like Pascal, real is float. So my suggestion would be just "into a numeric type".

expect("Please type a number!");

As before, a message like "Expected numeric input!" would be less confusing for beginners.

I hope this helps to further improve the readability of the book.

Best regards,

Dr. Stefan Salewski

@akauppi
Copy link

akauppi commented Dec 28, 2023

I think listing minuscle details around the book is easy.

What’s harder is simply making it shorter. While (my feeling) the books starts out great (even in Chapter 2 I had no concerns), in the middle it’s really, really heavy. Such larger editing should happen first, then tinier sentence level concerns can be considered.

The book deserves a weight loss, imho. I have my own, larger list if someone who’s worked longer with it wishes to have a look. :)

@StefanSalewski
Copy link
Author

Such larger editing should happen first,

I agree, the book might get a lot of edits, larger, and smaller ones. It is not perfect, but good enough as an introduction to the Rust language. I stopped reporting issues for now, as there are just too many. And many open issues and pull requests. Actually, I wonder why the book got 5 stars from most people at Amazon. Three stars like in https://www.amazon.com/Rust-Programming-Language-2nd/product-reviews/1718503105/ref=cm_cr_unknown?ie=UTF8&reviewerType=all_reviews&pageNumber=1&filterByStar=three_star is more realistic. Five stars is a bit unfair compared to other really good books. But still, I am thankful that the free online version exists, it enabled me to get a free introduction to Rust. If I would have been sure that a much better, non free introduction might exists, I think I would have bought that. But I still have no real idea about the quality of other Rust books, the Amazon star rating is not really meaningful. Actually, I know well how much work is was before tools like ChatGPT become available to write computer books, I wrote a Nim introduction book in 2020, see https://nimprogrammingbook.com/.

@PythonCoderUnicorn

This comment was marked as resolved.

@chriskrycho
Copy link
Contributor

chriskrycho commented Mar 29, 2024

Thanks for the reports, @StefanSalewski. There are, of course, plenty of places the book could be improved to be clearer or more precise. As I go through each of these examples, though, I think they ultimately come down to a matter of preference. In a number of cases the suggested alternatives are less clear, at least to my eye. (And we are definitely not going to use ChatGPT output!)

Moreover, given the amount of work involved in revisions to the paper text, we will default to keeping things the same unless it is fixing something which is obviously an error.

On expect() specifically, that is something we will evaluate. When the text was originally written, the error-reporting guidelines now present in the standard library docs (for example, on Option::expect and in std::error) had not yet been written. An update there might be nice, so we will discuss it!


@PythonCoderUnicorn, I have moved that to a separate issue. Thanks!

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

4 participants