Skip to content

Listing 2.3, run: integers and floats

Rust in Action, run › Chapter 2 › §2.3.1 · the book's file: ch2/ch2-intro-to-numbers.rs ↗

Level: 101 · a companion to Rust in Action, chapter 2

One line: Listing 2.3 prints 20 + 21 + 22 = 63, 1000000000000 and 42 on rustc 1.98.0, exactly as the book shows. The : i64 on one_million looks optional and is not: without it, the pow call does not compile, and with : i32 the result overflows. When the section talks about declaring a type's size in bytes, the type names actually give it in bits.

What the listing prints

ch2-intro-to-numbers.rs, run on rustc 1.98.0
20 + 21 + 22 = 63
1000000000000
42

Nine claims, run

Verified output of intro_to_numbers.rs — regenerated by tools/run_examples.py, never hand-typed.

1. 20 + 21 + 22 = 63
   twenty is i32; the same 20 added to an i64 is i64
2. i64: 1000000000000
   i32: checked_pow(2) is None, because i32::MAX is 2147483647
3. forty_twos is [f32; 3]; an unsuffixed pair is [f64; 2]
4. 42 | 04 | 042.0
5. 24.5_f32.round() = 25, f32::round(24.5) = 25, 24.5_f32.round_ties_even() = 24
6. i16: 16 bits, 2 bytes; i32: 32 bits, 4 bytes; i64: 64 bits, 8 bytes
7. 20 21 text
8. 42 | 42 | forty-two
9. forty_twos[0] = 42, forty_twos.get(3) = None
  1. Rust infers a type when you give none: twenty gets its type from the sum it joins. Here that is i32, which is also the fallback, so the listing cannot show where the type came from. The same 20 added to an i64 becomes an i64, which shows it. → Type inference
  2. The : i64 on one_million is needed. 1 000 000² is 10¹², far past i32::MAX. As an i32, checked_pow(2) returns None, and a plain pow(2) panics in a debug build (transcript below). With no annotation and no suffix, the call does not compile at all. → The bill for having a width, Signed overflow
  3. An array has one element type, and its suffixed members choose it. [42.0, 42f32, 42.0_f32] is [f32; 3], because the unsuffixed 42.0 takes the type of its neighbours. On their own, two unsuffixed floats fall back to f64. That is what the listing's note means by 32-bit or 64-bit depending on context. Mix an f32 with an f64 and the array does not compile. → Arrays and slices, Single and double precision
  4. {:02} pads to at least two characters with zeros, and 42 already has two, so in the listing it changes nothing. 4.0 shows it working, and {:05.1} adds a precision. → The format mini-language
  5. 24.5_f32.round() needs its suffix, and a path call does not. The book is right that a method call on a float literal needs a concrete type. Writing f32::round(24.5) gives it one through the parameter. round sends a tie away from zero (25); round_ties_even sends it to the even neighbour (24). → Making a float whole
  6. The number in a type's name counts bits, not bytes. The book says you get used to declaring a type's size in bytes. i32 is 32 bits, and size_of reports it as 4 bytes. → Values, Meet the byte
  7. "Conversions are always explicit" is true of numbers only. An i16 does not quietly become an i32: that is E0308, and the compiler suggests .into(). Some other conversions do happen without a call: &String becomes &str wherever a &str is expected. That is deref coercion, and the compiler's list of coercions has no numeric conversion on it. → Coercion, From and Into, Casting with as
  8. Operator overloading means each pair of types is a separate trait impl. + works on integers, floats and String + &str because each of those pairs has its own Add ↗ impl. i32 + i64 has none, so it does not compile, even though both are integers. → Operators are traits
  9. Indexing starts at 0, and get asks without the risk of a panic. An index past the end is a panic at run time. When both the array and the index are constants, it is a compile error instead. → Out of bounds is a panic

What the compiler refuses

With no type, pow does not compile

rustc 1.98.0 output for pow_on_a_bare_literal.rs
error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
 --> pow_on_a_bare_literal.rs:3:32
  |
3 |     println!("{}", one_million.pow(2));
  |                                ^^^
  |
help: you must specify a type for this binding, like `i32`
  |
2 |     let one_million: i32 = 1_000_000;
  |                    +++++

The fallback to i32 happens too late to pick a method, so the compiler asks for the type. Take its suggestion literally, i32, and you get the next section.

As an i32, the square overflows

Abridged — rustc 1.98.0 debug build of i32_million_squared.rs, run
thread 'main' panicked at …/library/core/src/num/mod.rs:426:5:
attempt to exponentiate with overflow

A release build (rustc -O) has no overflow checks, so it prints the wrapped value -727379968 and exits with status 0. The i64 in the listing avoids both. → Overflow checks, in the glossary

A method on a bare float literal

rustc 1.98.0 output for round_on_a_bare_float.rs
error[E0689]: can't call method `round` on ambiguous numeric type `{float}`
 --> round_on_a_bare_float.rs:2:25
  |
2 |     println!("{}", 24.5.round());
  |                         ^^^^^
  |
help: you must specify a concrete type for this numeric value, like `f32`
  |
2 |     println!("{}", 24.5_f32.round());
  |                        ++++

An f32 and an f64 in one array

Abridged — rustc 1.98.0 output for mixed_float_array.rs
error[E0308]: mismatched types
 --> mixed_float_array.rs:2:36
  |
2 |     let forty_twos = [42.0, 42f32, 42.0_f64];
  |                                    ^^^^^^^^ expected `f32`, found `f64`

No quiet widening

Abridged — rustc 1.98.0 output for i16_into_i32.rs
error[E0308]: mismatched types
 --> i16_into_i32.rs:3:21
  |
3 |     let long: i32 = short;
  |               ---   ^^^^^ expected `i32`, found `i16`
  |               |
  |               expected due to this
  |
help: you can convert an `i16` to an `i32`
  |
3 |     let long: i32 = short.into();
  |                          +++++++

+ between two integer types

Abridged — two errors in rustc 1.98.0 output for i32_plus_i64.rs
error[E0308]: mismatched types
 --> i32_plus_i64.rs:4:28
  |
4 |     println!("{}", small + big);
  |                            ^^^ expected `i32`, found `i64`

error[E0277]: cannot add `i64` to `i32`
   --> i32_plus_i64.rs:4:26
    |
  4 |     println!("{}", small + big);
    |                          ^ no implementation for `i32 + i64`
    |
    = help: the trait `Add<i64>` is not implemented for `i32`

A constant index past the end

rustc 1.98.0 output for const_index_out_of_bounds.rs
error: this operation will panic at runtime
 --> const_index_out_of_bounds.rs:3:23
  |
3 |     println!("{:02}", forty_twos[3]);
  |                       ^^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 3
  |
  = note: `#[deny(unconditional_panic)]` on by default

error: aborting due to 1 previous error

Concepts, keywords, topics

In the listing or the section Keyword or concept Lesson here Normative source
let twenty = 20; type inference, integer fallback Type inference Integer literal expressions ↗
let twenty_one: i32 = 21; type annotation What a type annotation does let statements ↗
22i32, 1_000_000 literal suffix, digit separator Writing a number down Suffixes ↗
one_million.pow(2) methods on primitives, overflow Meet the byte · Signed overflow i64::pow ↗ · checked_pow ↗
[42.0, 42f32, 42.0_f32] array, float literal, f32/f64 Arrays and slices · What a float actually stores Array expressions ↗ · Floating-point literals ↗
{:02} format spec: width, fill The format mini-language Width ↗
forty_twos[0] indexing Arrays and slices Indexing expressions ↗
24.5_f32.round() rounding a float Making a float whole f32::round ↗
the same + for many types operator overloading Operators are traits Arithmetic operators ↗
explicit conversions From/Into, as, coercion From and Into · Casting with as · Coercion Type coercions ↗
many numeric types integer and float widths Values · Tables 2.1 and 2.2, run Numeric types ↗

The glossary defines integer fallback, overflow checks, operator overloading, coercion, as cast, array and E0689.

See also

Po polsku

Listing 2.3 wypisuje na rustc 1.98.0 dokładnie to, co pokazuje książka. Adnotacja : i64 przy one_million tylko wygląda na opcjonalną: bez niej wywołanie pow się nie kompiluje (E0689, can't call method on ambiguous numeric type), a jako i32 wynik 10¹² przekracza i32::MAX i w kompilacji debug kończy się paniką attempt to exponentiate with overflow. Liczba w nazwie typu to bity, nie bajty: i32 ma 32 bity, czyli 4 bajty.

Tablica ma jeden typ elementów i wybierają go elementy z przyrostkiem: [42.0, 42f32, 42.0_f32] to [f32; 3], a dwie liczby bez przyrostka dają f64. Konwersje liczbowe są zawsze jawne (i16 nie stanie się po cichu i32), ale nie wszystkie konwersje — &String zamienia się w &str bez żadnego wywołania (deref coercion). Przeciążanie operatorów (operator overloading) oznacza osobną implementację cechy Add dla każdej pary typów, dlatego i32 + i64 się nie kompiluje. round zaokrągla połówki od zera (24,5 → 25), a round_ties_even do parzystej (24).

Szukaj po polsku: przepełnienie liczby całkowitej · zaokrąglanie w Ruscie · rust E0689 ambiguous numeric type · rust i32 + i64 cannot add · przeciążanie operatorów