Listing 2.2, run: variables and functions¶
Rust in Action, run › Chapter 2 › §2.2.1 · the book's file: ch2/ch2-first-steps.rs ↗
Level: 101 · a companion to Rust in Action, chapter 2
One line: Listing 2.2 compiles unchanged on rustc 1.98.0 and prints ( a + b ) + ( c + d ) = 90, not the a + b = 30 promised by the paragraph that introduces it. Its notes hold up, with one exception: a semicolon after i + j does not make add return (). It makes add fail to compile.
What the listing prints¶
The book's file, compiled as it ships (edition 2024, no warnings):
e is add(add(10, 20), add(30, 30)). The a + b = 30 in the introduction describes a smaller program than the one printed under it.
Eight claims, run¶
Verified output of first_steps.rs — regenerated by tools/run_examples.py, never hand-typed.
1. the listing's e = 90
2. a is i32, wide is i64, alone is i32
3. b is i32, c is i32, d is i32, c == d: true
4. size was assigned once, after its let, with no mut: big
5. println! is an expression...
...whose value is (); format! gives a String: "90"
6. add is below main, double is inside it: 6
7. sign(-5) = negative, sign(5) = not negative
8. 'é' is a char of 4 bytes; "é" is a &str holding 2 bytes
- The 90. The first line recomputes the listing's
efrom its four values. - "Types can be inferred" — from the use, not from the literal.
aisi32because it is passed toadd(i: i32, …). The same10passed to a function that takesi64is ani64. Only a literal that nothing constrains falls back toi32. → Type inference, the two fallbacks let b: i32,30i32and30_i32write one type three ways. The annotation goes on the name, the suffix on the number, and the_before a suffix is only there to make it easier to read. The book says that at times the annotation is required. The case everybody meets first is"42".parse()with no target type, which isE0284. → What a type annotation does, the suffix is the type- Immutable means assigned once, not assigned on the
letline.let size;followed by one assignment in each branch compiles withoutmut. A second assignment to the same name isE0384. A secondletis a new variable (shadowing) and needs nomuteither. → Variables, The shadowing map - A macro call becomes code, and the code has a value. The book says a macro returns code rather than values. The expansion happens at compile time, and what it expands into is an ordinary expression:
println!(…)is worth()andformat!(…)is worth aString. → Macros - Items can go in any order.
addsits belowmainin the listing, and afnmay even be declared inside another function's body.letbindings work the other way: a name exists only from itsletdownwards. - The last expression is the return value, and
returnstill exists for leaving early, which is what the first line ofsigndoes. → A block is an expression - Single quotes make a
char, a type of its own. Acharis one Unicode scalar value and always four bytes. The sameéin double quotes is a&strof two UTF-8 bytes. Not everything a reader sees as one character fits in achar; see the refusal below. → Meet thechar, Whycharis 32 bits
What the compiler refuses¶
Every transcript here is rustc 1.98.0 compiling a file with the name shown in the title.
A semicolon after i + j is an error, not a unit return¶
The note under the listing says a semicolon here makes add return () instead of i32. With -> i32 still in the signature, rustc refuses:
error[E0308]: mismatched types
--> semicolon_on_the_tail.rs:5:27
|
5 | fn add(i: i32, j: i32) -> i32 {
| --- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
6 | i + j;
| - help: remove this semicolon to return this value
The body's last line is now a statement, so the body is worth (), and the signature promised an i32. A function returns () only when its signature has no ->, and that function refuses a last line with a value, the same error the other way round:
error[E0308]: mismatched types
--> no_return_type.rs:6:5
|
5 | fn add(i: i32, j: i32) {
| - help: try adding a return type: `-> i32`
6 | i + j
| ^^^^^ expected `()`, found `i32`
→ The semicolon is the switch, The unit type ()
Parameters need types; the return type does not¶
The book says type declarations are required when defining functions. That is true of every parameter:
error: expected one of `:`, `@`, or `|`, found `,`
--> untyped_parameters.rs:5:9
|
5 | fn add(i, j) -> i32 {
| ^ expected one of `:`, `@`, or `|`
|
help: if this is a parameter name, give it a type
|
5 | fn add(i: TypeName, j) -> i32 {
| ++++++++++
The return type may be left out, and leaving it out means -> (), which is the second transcript above.
{} is checked against Display at compile time¶
The book says println! works out which methods to call on its arguments. It does that while compiling: {} requires Display ↗, {:?} requires Debug, and a type without the one you asked for does not compile:
error[E0277]: `Vec<{integer}>` doesn't implement `std::fmt::Display`
--> display_a_vec.rs:3:20
|
3 | println!("{}", v);
| -- ^ `Vec<{integer}>` cannot be formatted with the default formatter
| |
| required by this formatting parameter
|
= help: the trait `std::fmt::Display` is not implemented for `Vec<{integer}>`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
→ Debug and Display, The format mini-language
One char is one code point, not one visible character¶
error: character literal may only contain one codepoint
--> two_codepoint_char.rs:2:18
|
2 | let thumbs = '\u{1F44D}\u{1F3FD}';
| ^^^^^^^^^^^^^^^^^^^^
|
help: if you meant to write a string literal, use double quotes
A thumbs-up with a skin tone is two code points, so it needs double quotes. → Grapheme clusters, in Meet the char
main starts a binary, not every crate¶
A binary without main is E0601 ↗, "main function not found in crate". The same file compiles with rustc --crate-type lib, because a library has no entry point.
The book's footnote says main returns () or a Result. The actual rule is any type that implements Termination ↗. That includes ExitCode: fn main() -> ExitCode { ExitCode::from(3) } exits with status 3. A String is refused with E0277, "main can only return types that implement Termination". → Running a scratch program
Line numbers and names in the prose¶
- The text on p. 36 says line 5 is
let c = 30_i32;. In the listing, line 4 islet c = 30i32;and line 5 islet d = 30_i32;. - The same page puts
fn add(…) -> i32 {on line 10. It is line 11; line 10 is blank. - The book calls
->"the dagger" or "thin arrow". Neither the Book nor the Reference uses either name: the Book's operator appendix ↗ lists it as the function and closure return type, and the Reference's grammar ↗ names-> TypeaFunctionReturnType. - The book calls code blocks "lexical scopes". A block is a scope and an expression, and the second job is what makes claim 7 work.
Concepts, keywords, topics¶
| In the listing | Keyword or concept | Lesson here | Normative source |
|---|---|---|---|
fn main() { |
fn, the entry point |
Functions · Running a scratch program | Main functions ↗ |
let a = 10; |
let, binding, type inference |
Variables · Type inference | let statements ↗ |
let b: i32 = 20; |
type annotation, i32 |
What a type annotation does · Values | Integer types ↗ |
30i32, 30_i32 |
literal suffix, digit separator | Writing a number down | Integer literals ↗ |
add(add(a, b), add(c, d)) |
call expression | Functions | Call expressions ↗ |
println!("…{}", e) |
macro, Display, format string |
Macros · Debug and Display · The format mini-language | Macros ↗ · std::fmt ↗ |
-> i32 |
return type | The unit type () |
Functions ↗ |
i + j |
tail expression, block expression | A block is an expression | Block expressions ↗ |
; |
statement | The semicolon is the switch | Expression statements ↗ |
mut, by its absence |
immutability, shadowing | The shadowing map | — |
'a' versus "a", on p. 36 |
char, &str |
Meet the char · String vs &str |
Character literals ↗ |
Short definitions of type annotation, integer fallback, tail expression, unit type, binding mutability, shadowing, char, E0284 and E0308 are in the glossary. Every error code above has its home lesson in the error index.
See also¶
- Rust in Action, run — every listing checked so far, and a concept index across them
- Next: Listing 2.3, run: integers and floats — the same inference, with numbers that overflow
- Books — what this library thinks of Rust in Action, and which book to read beside it
Po polsku¶
Listing 2.2 kompiluje się na rustc 1.98.0 bez zmian i bez ostrzeżeń, ale wypisuje ( a + b ) + ( c + d ) = 90, a nie zapowiadane w tekście a + b = 30. Sprawdzone twierdzenia: typ zmiennej bez adnotacji (type inference) wynika z użycia — a jest i32, bo trafia do add(i: i32, …), a ta sama 10 przekazana funkcji z i64 staje się i64. Adnotacja : i32, przyrostek 30i32 i 30_i32 to jeden typ zapisany na trzy sposoby. Niemutowalność znaczy jedno przypisanie: let size; i przypisanie później kompiluje się bez mut.
Najważniejsza poprawka dotyczy notatki o średniku. Średnik po i + j nie sprawia, że add zwraca () — przy -> i32 w sygnaturze funkcja przestaje się kompilować (E0308, mismatched types), a kompilator sam podpowiada usunięcie średnika. Makro (println!) rozwija się w kod w czasie kompilacji, a ten kod ma wartość: println! daje (), format! daje String. Pojedyncze cudzysłowy tworzą char — jeden punkt kodowy Unicode, zawsze cztery bajty — więc kciuk z odcieniem skóry (dwa punkty kodowe) wymaga cudzysłowów podwójnych.
Szukaj po polsku: wnioskowanie typów w Ruscie · adnotacja typu · wyrażenie końcowe bloku · rust E0308 remove this semicolon · rust main ExitCode Termination