5.6 Numeric Literals and Their Default Type
In Rust, numeric literals are used to define values for different numeric types, such as integers and floating-point numbers. One of the key features of Rust’s type system is that it requires numeric types to be explicitly stated or inferred by the compiler, meaning that every literal is assigned a type either based on the context or its default type.
5.6.1 Integer Literals
By default, an integer literal without a suffix is inferred as an i32
. However, Rust provides several ways to specify a literal’s type explicitly using suffixes, such as:
123i8
for a signed 8-bit integer123u64
for an unsigned 64-bit integer
You can also use type annotations when declaring a variable:
#![allow(unused)] fn main() { let x = 123u16; // Literal with a suffix let y: u16 = 123; // Type annotation }
Rust supports the use of underscores to make large numbers more readable:
#![allow(unused)] fn main() { let large_num = 1_000_000; // Inferred as i32 }
5.6.2 Floating-Point Literals
Floating-point literals default to f64
for precision and performance reasons. As with integers, the type can be explicitly defined using a suffix, for example:
#![allow(unused)] fn main() { let pi = 3.14f32; // 32-bit floating point let e = 2.718; // Inferred as f64 }
It's important to note that assigning an integer directly to a floating-point variable, such as let a: f64 = 10;
, is invalid in Rust because 10
is treated as an integer literal. Instead, you must use a floating-point literal, like 10.0
.
However, floating-point literals can be written without a fractional part. For example, 1.
is treated as 1.0
, similar to C:
#![allow(unused)] fn main() { let x = 1.; // Equivalent to 1.0 }
Unlike in C, Rust does not allow omitting the digit before the decimal point. Therefore, .7
is not a valid floating-point literal in Rust. Instead, you must write it as 0.7
:
This requirement ensures clarity in floating-point literals, avoiding potential confusion in code.
5.6.3 Hexadecimal, Octal, and Binary Literals
Rust supports other number systems for literals, which can be useful for low-level programming:
- Hexadecimal: Prefix with
0x
- Example:
let hex = 0xFF;
- Example:
- Octal: Prefix with
0o
- Example:
let octal = 0o77;
- Example:
- Binary: Prefix with
0b
- Example:
let binary = 0b1010;
- Example:
Example:
fn main() { // editable example let decimal = 255; let hex = 0xFF; let octal = 0o377; let binary = 0b1111_1111; let byte = b'A'; // Byte literal println!("Decimal: {}", decimal); println!("Hexadecimal: {}", hex); println!("Octal: {}", octal); println!("Binary: {}", binary); println!("Byte: {}", byte); }
5.6.4 Type Inference
While Rust allows type inference, it's important to note that certain operations may require explicit type annotations, especially in cases where a literal could be interpreted in multiple ways.
Example:
fn main() { let x = 42; // Inferred as i32 let y = 3.14; // Inferred as f64 let z = x as f64 + y; // Type casting x to f64 println!("Result: {}", z); }
In this example, we cast x
to f64
to match the type of y
for the addition operation.