Rust Drills based on The Rust Programming Language book

Heylo all, so I am summarizing 'The Book' in an interactive way. Instead of reading through summaries the conventional way, you get challenged with questions first, and then read the answers.
The problem is, the drills need a new home for proper organization. You can checkout this page for sample.
Please help. Thank you.

3 Likes

Is that Comic Sans?

  1. What do expressions implicitly return when they don’t have an explicitly returned value?

A unit value

This is wrong, an expression always has an explicit return type. There is no such thing an implicitly returned value. Rust in general is quite averse to anything implicit. It's also unclear what kind of expressions are even implied in this question. For example, return and break do not implicitly return (), they have type !. I have no idea what else could be called "implicitly returned value".

  1. What is a Rust tuple?

A tuple is a compound type that gives you the means to group together a number of values of the same type only into one. It has a fixed length, so they cannot grow or shrink in size.

You're thinking about an array. A tuple groups elements of arbitrary types.

  1. Relying on integer overflow’s wrapping behavior is considered what?

That... is a very strangely worded question.

  1. How does Rust react of integer overflow scenerios?

I think it makes sense to tell here that the overflow behaviour is really controlled by the -C overflow-checks rustc option, and by the overflow-checks option in the Cargo profile section. It's important that it's possible to change the overflow behaviour regardless of the profile.

  1. In what primary situation would you use integer types isize or usize?

When indexing some sort of collection.

isize isn't used to index collections. The primary reason to use isize is as a difference between usize elements, or pointer offsets.

  1. What is the validity period of a Rust constant variable?

The entire time a Rust program runs. It seems to me that this means that they are not garbage collected.

Nothing in Rust is ever garbage collected, it's not a GC-based language. So it's strange to ever make such a remark.

Also, it doesn't make sense to speak of the "validity range" of a constant (you also shouldn't call it "variable", it can't vary). A static item has a validity range: it lives in memory for as long as the program runs. A const item doesn't have any place in memory, and thus speaking of its validity is meaningless. A const is effectively inlined at the point in source where it is used (though the compiler may decide to promote it to a static item as an optimization and in certain required cases). In particular, you can't really take an address of a constant. If you do, the constant will be promoted to a static, which may very well be different at different points in code, while an explicitly static item will have the same address everywhere.

  1. What syntax is used as a placeholder inside strings, when you plan to access a variable’s value inside the string?

This is confusingly worded. The {} pair has no meaning for strings in general. It is used specifically in string literals which are used in the format_args! macro and other macros which delegate to it (format!, print!, eprint!, log! etc). If you just make an arbitrary string with braces, you can't use it in the formatting macros and you can't interpolate the variable value without using some other crates.

  1. Variables in Rust are immutable by default. How can you make a mutable Rust variable?

By using the keyword “mut” in your variable declaration, like this:

let mut variable-name = 5;

That's invalid syntax.

  1. Cargo command “cargo check” compiles your code, but does it also generate or produce an execuatable?

It doesn't compile your code. That's where the speed benefits come from, and that's why there is no final executable. It only performs the type checking, analysis and linting of your code. Technically, cargo check computes only the crate metadata, which consists of the public signatures of items in the crate (but not e.g. the bodies of functions).

6 Likes

I believe this is referring to how a block expression returns () when it has no final operand following its statements; that is, { a; b; } is equivalent to { a; b; () }. So perhaps the question could be clarified by referencing block expressions in particular.

2 Likes

No, that is 'Architects Daughter' font. It came with the Jekyll 'Architect' theme I used.
The font family is: "Architects Daughter", "Helvetica Neue", Helvetica, Arial, serif;

Thank you for lending the eagle eyes. I have taken note of your corrections and updated the content as well.

52. before:

  1. What do expressions implicitly return when they don't have an explicitly returned value?

A unit value

52. now:

  1. What do functions return when they don't have an explicitly returned value or lifetime annotated reference?

A unit value, as in;
()

46. before:

  1. What is a Rust tuple?

A tuple is a compound type that gives you the means to group together a number of values of the same type only into one.

It has a fixed length, so they cannot grow or shrink in size.

46. now:

  1. What is a Rust tuple?

A tuple is a compound type that gives you the means to group together a number of values of arbitrary types into one. It has a fixed length, so they cannot grow or shrink in size.

30. before:

  1. Relying on integer overflow's wrapping behavior is considered what?
    An error.

30. now:

  1. Relying on two's complement wrapping in overflow, for code in release mode could result to code with unexpected behavior, true or false?

True.

29. before:

  1. How does Rust react of integer overflow scenarios?

In debug mode: Your program will panic at runtime and then exit with an error, when it checks your program for integer overflow.

In release mode: It won't panic. Rather, two's complement wrapping occurs. For example, in the case of a u8, the value 256 becomes 0. In case of u8 with an ascribed value 257, becomes 1.

29. now:

  1. How does Rust handle integer overflow in your code?

In debug mode: Your program will panic at runtime and then exit with an error, when it checks your program for integer overflow.

In release mode: It won't panic. Rather, two's complement wrapping occurs. For example, in the case of a u8, the value 256 becomes 0. In case of u8 with an ascribed value 257, becomes 1.

N.B: Overflow behavior is actually controlled by the '-C overflow-checks' rustc option, and by the 'overflow-checks' option in the Cargo profiles section. You really can change the overflow behavior regardless of the profile.

27. before:

  1. In what primary situation would you use integer types isize or usize?
    When indexing some sort of collection.

27. now:

  1. In what primary situation would you use integer types isize or usize?

They are used as pointer offsets to any location in memory. isize is an alternative to usize.
The difference between isize and usize is that isize is a pointer-sized signed integer type, while usize is a pointer-sized unsigned integer type.

They both have sizes which depends on the arch of your machine (32bit or 64bit)

6. before:

  1. What is the validity period of a Rust constant variable?

The entire time a Rust program runs. It seems to me that this means that they are not garbage collected.

6. now:

  1. What are the major characteristics of a constant?
  • A constant in Rust is unchangeable and would remain so for as long as the program runs.
  • A constant's value would be etched into the resulting code compilation.
  • The compiler may decide to promote it to a 'static lifetime as an optimization step, once you begin to access the constant declaration's address elsewhere in the code, within a scope equal, or less than where the constant declaration was made.

2. before:

  1. What syntax is used as a placeholder inside strings, when you plan to access a variable's value inside the string?

2. now

  1. What operator is Rust's default string-literal formatter?

1. before:

  1. Variables in Rust are immutable by default. How can you make a mutable Rust variable?

By using the keyword "mut" in your variable declaration, like this:
let mut variable-name = 5;

1. now:

  1. Variables in Rust are immutable by default. How can you make a mutable Rust variable?

By using the keyword "mut" in your variable declaration, like this:
let mut variable_name = 5;
println!("variable_name's value is: {}", variable_name);
variable_name = 7;
println!("variable_name now is: {}", variable_name);

22. before:

  1. Cargo command "cargo check" compiles your code, but does it also generate or produce an executable?

No

22. now:

  1. What does Cargo command "cargo check" do?

It performs type checking, analysis, and linting of your code. Technically, "cargo check" computes only the crate metadata, which consists of the public signatures of items in the crate (but no, e.g. the bodies of functions).

I changed the question to be about functions instead

That is still incorrect. A function returns the thing that is specified in its return type, and it's unaffected by the structure of its inner statements. It is more correct if you change "a function" into "a block expression", but still feels off, since block expressions cannot use the explicit "return foo" syntax. Some people consider the "return last expression" convention to be an implicit return.

Maybe it is better if you change "function" into "closure". A closure's return type can be inferred, and it can have return expressions inside. But at that point imho that's more complicated than what you're trying to teach with these questions.

There is a lot of discussion going on whether usize and isize are really pointer-sized (i.e. a pointer can be round-tripped through an usize) or just offset-sized. Even though a lot of code and literature already assumes the former, personally I would prefer to leave out the specifics of usize size out of introductory texts. Most of the time casting pointers to usize is an antipattern anyway.

I would suggest to word it differently.

But it's not an operator. It's a specific syntax within the template string of the format! macro. I could write a proc macro or just a function which uses a different syntax (e.g. the oldschool %-formatting), and it would be just as valid.

About 52, I wrote that because it is possible to write a function that has no explicitly state returned value or reference, but to Rust, the function still returns (), a unit. I would agree the "return last expression" to be valid.
I think you also see where I'm going with all these questions. I'm creating a tree of knowledge of learners who learn by principles. You may even call it First principles - they need to know the red wire from the green, sort of. I wouldn't want to trip them off by introducing non-beginner concepts early on.
An advanced Rust user like yourself may also benefit, in manner of back-to-the-basics revision, I hope.

Maybe etched could be rewritten to compiled, do you agree?