5/5
## Understanding Rust's Scalar Data Types In Rust, scalar data types represent a single value. These are the fundamental building blocks for more complex data structures and program logic. Rust provides four primary scalar types: integers, floating-point numbers, booleans, and characters. Each type has specific characteristics and use cases, which we'll explore in detail. ## Integers: Whole Numbers in Rust Integers are numerical values without any fractional or decimal component. Rust offers a comprehensive set of integer types, distinguished by their size (the number of bits they occupy in memory) and whether they are signed (can represent negative numbers) or unsigned (can only represent non-negative numbers). ### Signed Integers (`i8`, `i16`, `i32`, `i64`, `i128`) Signed integers can store both positive and negative whole numbers. The `i` in their type names stands for "integer," and the subsequent number indicates the bit size. * **`i8`**: 8-bit signed integer. * **`i16`**: 16-bit signed integer. * **`i32`**: 32-bit signed integer (this is the default integer type if not specified). * **`i64`**: 64-bit signed integer. * **`i128`**: 128-bit signed integer. The range of an n-bit signed integer is from `-(2^(n-1))` to `2^(n-1) - 1`. ```rust // Signed integers // Range: -(2^(n-1)) to 2^(n-1) - 1 let i0: i8 = -1; // Range: -128 to 127 let i1: i16 = 2; // Range: -32,768 to 32,767 let i2: i32 = 3; // Range: -2,147,483,648 to 2,147,483,647 let i3: i64 = -4; // Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 let i4: i128 = 5; // A very large range ``` ### Unsigned Integers (`u8`, `u16`, `u32`, `u64`, `u128`) Unsigned integers can only store non-negative whole numbers (zero and positive numbers). The `u` in their type names stands for "unsigned," and the number indicates the bit size. * **`u8`**: 8-bit unsigned integer. * **`u16`**: 16-bit unsigned integer. * **`u32`**: 32-bit unsigned integer. * **`u64`**: 64-bit unsigned integer. * **`u128`**: 128-bit unsigned integer. The range of an n-bit unsigned integer is from `0` to `2^n - 1`. ```rust // Unsigned integers // Range: 0 to 2^n - 1 let u0: u8 = 1; // Range: 0 to 255 let u1: u16 = 2; // Range: 0 to 65,535 let u2: u32 = 3; // Range: 0 to 4,294,967,295 let u3: u64 = 4; // Range: 0 to 18,446,744,073,709,551,615 let u4: u128 = 5; // A very large range, up to 2^128 - 1 ``` ### Architecture-Dependent Integers (`isize`, `usize`) Rust also includes integer types whose size depends on the architecture of the computer on which the program is compiled and run. * **`isize`**: A signed integer whose size matches the pointer size of the target architecture. * **`usize`**: An unsigned integer whose size matches the pointer size of the target architecture. On a 32-bit architecture, `isize` is equivalent to `i32`, and `usize` is equivalent to `u32`. On a 64-bit architecture, `isize` is `i64`, and `usize` is `u64`. The `usize` type is particularly significant because it's used by Rust for indexing into collections like arrays and vectors, and for representing memory sizes and counts of items. ```rust // Architecture-dependent integers let i5: isize = -6; // Will be i32 or i64 let u5: usize = 6; // Will be u32 or u64 ``` ## Floating-Point Numbers: Handling Decimals Floating-point numbers are used to represent numbers that have a decimal point. Rust provides two primitive types for floating-point numbers: * **`f32`**: A single-precision float, occupying 32 bits. * **`f64`**: A double-precision float, occupying 64 bits. If you declare a floating-point number without explicitly specifying its type, Rust defaults to `f64` because its precision is generally more suitable for most calculations. Both `f32` and `f64` types adhere to the IEEE 754 standard for floating-point arithmetic. ```rust // Floating point numbers let f0: f32 = 0.01; let f1: f64 = 0.02; // f64 is the default if not specified ``` ## Booleans: Truth Values in Rust The boolean type in Rust is `bool`. It is one of the simplest scalar types, as it can only have two possible values: * `true` * `false` Booleans are primarily used for conditional logic (e.g., in `if` statements). A boolean value occupies one byte in memory. ```rust // Boolean let b: bool = true; let is_active: bool = false; ``` ## Characters: Representing Single Unicode Values Rust's `char` type is designed to represent a single Unicode Scalar Value. This means a `char` can hold much more than just basic ASCII characters. It can represent accented letters, characters from various global languages, emojis, and even control characters. Character literals are specified using single quotes (`'`). This distinguishes them from string literals, which use double quotes (`"`). A `char` in Rust is four bytes in size, allowing it to encompass the full range of Unicode scalar values. ```rust // Characters let c: char = 'c'; let z: char = 'ℤ'; let heart: char = '❤'; let e: char = '🦀'; // Emojis are valid char values // Note: "c" (with double quotes) would be a string slice (&str), not a char. ``` ## Explicit Type Conversion with `as` Rust is a statically-typed language that prioritizes type safety. To prevent potential bugs that can arise from unexpected type coercions, Rust does not perform implicit type conversions (also known as casting) between primitive types. If you need to convert a value from one primitive type to another, you must do so explicitly using the `as` keyword. When converting, be aware that the underlying bit pattern of the value might be reinterpreted. This can lead to different numerical values, especially when converting between signed and unsigned integers or when converting a larger type to a smaller type (which can cause truncation if the value is out of range for the target type). For example, converting a negative signed integer to an unsigned integer will result in a large positive number. This is due to how negative numbers are typically represented in memory (e.g., using two's complement). ```rust // Type conversion let i: i32 = -1; let u: u32 = i as u32; // Explicit conversion from i32 to u32 // To see the result, you would typically print it: // println!("({i}) as u32 = ({u})"); ``` If you were to run this and print the values, the output would be: ```text (-1) as u32 = (4294967295) ``` This is because `-1` in `i32` (using two's complement) is represented by all bits being set to `1`. When these bits are reinterpreted as a `u32`, they represent the maximum possible value for a `u32`. ## Discovering Numeric Type Limits: MIN and MAX Rust provides a convenient way to determine the minimum and maximum values that a specific numeric type can represent. This is achieved by using associated constants `MIN` and `MAX` on the type itself. For example, to get the maximum value for an `i32` or the minimum value for a `u32`: ```rust // Min and max values for numeric types let i_max: i32 = i32::MAX; let u_min: u32 = u32::MIN; // Example of printing these values: // println!("i32 max: {i_max}"); // println!("u32 min: {u_min}"); ``` Running this and printing the values would produce: ```text i32 max: 2147483647 u32 min: 0 ``` This is extremely useful for understanding the bounds of your data and for validation. When experimenting with these examples, you might place them in a `fn main() { ... }` block within a Rust file (e.g., `examples/scalar.rs` in a Cargo project). You can use the `#![allow(unused)]` attribute at the top of your file to suppress compiler warnings about unused variables if you're just declaring them for demonstration. The code can then be compiled and run, for instance, using `cargo run --example scalar` if it's structured as an example in a Cargo project. Understanding these scalar types—their properties, ranges, memory footprints, and conversion rules—is fundamental to writing effective, correct, and efficient Rust programs. They form the basis upon which all other data structures and operations are built.
In Rust, scalar data types represent a single value. These are the fundamental building blocks for more complex data structures and program logic. Rust provides four primary scalar types: integers, floating-point numbers, booleans, and characters. Each type has specific characteristics and use cases, which we'll explore in detail.
Integers are numerical values without any fractional or decimal component. Rust offers a comprehensive set of integer types, distinguished by their size (the number of bits they occupy in memory) and whether they are signed (can represent negative numbers) or unsigned (can only represent non-negative numbers).
i8
, i16
, i32
, i64
, i128
)Signed integers can store both positive and negative whole numbers. The i
in their type names stands for "integer," and the subsequent number indicates the bit size.
i8
: 8-bit signed integer.
i16
: 16-bit signed integer.
i32
: 32-bit signed integer (this is the default integer type if not specified).
i64
: 64-bit signed integer.
i128
: 128-bit signed integer.
The range of an n-bit signed integer is from -(2^(n-1))
to 2^(n-1) - 1
.
u8
, u16
, u32
, u64
, u128
)Unsigned integers can only store non-negative whole numbers (zero and positive numbers). The u
in their type names stands for "unsigned," and the number indicates the bit size.
u8
: 8-bit unsigned integer.
u16
: 16-bit unsigned integer.
u32
: 32-bit unsigned integer.
u64
: 64-bit unsigned integer.
u128
: 128-bit unsigned integer.
The range of an n-bit unsigned integer is from 0
to 2^n - 1
.
isize
, usize
)Rust also includes integer types whose size depends on the architecture of the computer on which the program is compiled and run.
isize
: A signed integer whose size matches the pointer size of the target architecture.
usize
: An unsigned integer whose size matches the pointer size of the target architecture.
On a 32-bit architecture, isize
is equivalent to i32
, and usize
is equivalent to u32
. On a 64-bit architecture, isize
is i64
, and usize
is u64
.
The usize
type is particularly significant because it's used by Rust for indexing into collections like arrays and vectors, and for representing memory sizes and counts of items.
Floating-point numbers are used to represent numbers that have a decimal point. Rust provides two primitive types for floating-point numbers:
f32
: A single-precision float, occupying 32 bits.
f64
: A double-precision float, occupying 64 bits.
If you declare a floating-point number without explicitly specifying its type, Rust defaults to f64
because its precision is generally more suitable for most calculations. Both f32
and f64
types adhere to the IEEE 754 standard for floating-point arithmetic.
The boolean type in Rust is bool
. It is one of the simplest scalar types, as it can only have two possible values:
true
false
Booleans are primarily used for conditional logic (e.g., in if
statements). A boolean value occupies one byte in memory.
Rust's char
type is designed to represent a single Unicode Scalar Value. This means a char
can hold much more than just basic ASCII characters. It can represent accented letters, characters from various global languages, emojis, and even control characters.
Character literals are specified using single quotes ('
). This distinguishes them from string literals, which use double quotes ("
). A char
in Rust is four bytes in size, allowing it to encompass the full range of Unicode scalar values.
as
Rust is a statically-typed language that prioritizes type safety. To prevent potential bugs that can arise from unexpected type coercions, Rust does not perform implicit type conversions (also known as casting) between primitive types. If you need to convert a value from one primitive type to another, you must do so explicitly using the as
keyword.
When converting, be aware that the underlying bit pattern of the value might be reinterpreted. This can lead to different numerical values, especially when converting between signed and unsigned integers or when converting a larger type to a smaller type (which can cause truncation if the value is out of range for the target type).
For example, converting a negative signed integer to an unsigned integer will result in a large positive number. This is due to how negative numbers are typically represented in memory (e.g., using two's complement).
If you were to run this and print the values, the output would be:
This is because -1
in i32
(using two's complement) is represented by all bits being set to 1
. When these bits are reinterpreted as a u32
, they represent the maximum possible value for a u32
.
Rust provides a convenient way to determine the minimum and maximum values that a specific numeric type can represent. This is achieved by using associated constants MIN
and MAX
on the type itself.
For example, to get the maximum value for an i32
or the minimum value for a u32
:
Running this and printing the values would produce:
This is extremely useful for understanding the bounds of your data and for validation.
When experimenting with these examples, you might place them in a fn main() { ... }
block within a Rust file (e.g., examples/scalar.rs
in a Cargo project). You can use the #![allow(unused)]
attribute at the top of your file to suppress compiler warnings about unused variables if you're just declaring them for demonstration. The code can then be compiled and run, for instance, using cargo run --example scalar
if it's structured as an example in a Cargo project.
Understanding these scalar types—their properties, ranges, memory footprints, and conversion rules—is fundamental to writing effective, correct, and efficient Rust programs. They form the basis upon which all other data structures and operations are built.
A clear primer on Understanding Rust's Scalar Data Types - Explore Rust's fundamental scalar types: integers (signed/unsigned, arch-dependent), floating-point numbers, booleans, and Unicode characters. Understand explicit type conversion with `as` and how to query `MIN`/`MAX` constants for value boundaries.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Introduction to the Rust programming language
Rust variables and functions
Scalar types, arrays, strings, enum, structs, vectors, and hash maps in Rust
Rust control flows: If / else, if let and let else, loop, match
Rust ownership, including borrow and references
Rust error handling
Rust Modules
Rust Traits
Last updated on July 11, 2025
Rust Developer
Rust Programming BasicsDuration: 6min
Duration: 18min
Duration: 47min
Duration: 15min
Duration: 19min
Duration: 8min
Duration: 12min
Duration: 46min
Duration: 14min
Course Overview
About the course
Introduction to the Rust programming language
Rust variables and functions
Scalar types, arrays, strings, enum, structs, vectors, and hash maps in Rust
Rust control flows: If / else, if let and let else, loop, match
Rust ownership, including borrow and references
Rust error handling
Rust Modules
Rust Traits
Last updated on July 11, 2025