1. Scalar Types
The scalar type represents a separate value. There are four basic scalar types in Rust:Integer(integer)、Floating point number(floating-point number)、Boole(boolean) andcharacter(character). These types are common in most programming languages.
1. Integer Types
An integer is a number without a fractional part. In the previous tutorial on guessing numbers, we used itu32
. This type declaration means that the value is aUnsigned(unsigned) 32-bit integer (if it is signed, it willi
Beginning, e.g.i32
)。
The following table shows all built-in integer types in Rust, each type is either signed or unsigned, and has a clear bit size.
length | Signed | Unsigned |
---|---|---|
8-bit | i8 |
u8 |
16-bit | i16 |
u16 |
32-bit | i32 |
u32 |
64-bit | i64 |
u64 |
128-bit | i128 |
u128 |
arch | isize |
usize |
- Signed(signed) means that the value may be positive or negative, so the sign bit is required when storing;
- Unsigned(unsigned) only represents non-negative numbers (0 or positive numbers) and does not require sign bits.
For signed integers, if the type isi8
, it can store values from -128 to 127; if soi16
, the scope will be expanded accordingly, and so on. Unsigned types start from 0. For exampleu8
Can represent 0 to 255.
isize
andusize
Varies depending on the system architecture: 64-bit on a 64-bit architecture and 32-bit on a 32-bit architecture. These types are often used in scenarios such as indexing or memory size calculation based on the system architecture.
1.1 Overall digital surface volume
In Rust, various forms can be used to express integer literals, as shown in the following table:
Number literal form | Example |
---|---|
Decimal | 98_222 |
hexadecimal | 0xff |
Octal | 0o77 |
Binary | 0b1111_0000 |
Bytes (onlyu8 ) |
b'A' |
Notice:
- Underlines can be used in numbers
_
As a separator to improve readability, e.g.1_000
and1000
equivalence. - If you need to specify a type, you can add a type suffix to the number, for example
57u8
。
Usually if you are not sure what type of integer to use,Rust is used by defaulti32
. If you need to index the system architecture and other scenarios, you will only consider using it.isize
orusize
。
1.2 Integer Overflow
Suppose we have oneu8
A variable of type, the range of values it can represent is[0, 255]
. If you try to assign it to 256, it will happenInteger overflow(integer overflow), causing one of two behaviors:
- Debug mode compilation: Rust will perform an overflow check, and once it is found, it will be runpanic(The program crashes and exits).
-
Release mode compilation: Rust does not perform overflow checks, but performsTwo's complement surround(two’s complement wrapping). In other words, when the maximum representable value is exceeded, it will "surround" back to the minimum value. For example,
u8
Type, 256 will become 0, 257 will become 1, etc. Panic will not appear, but the results are often inconsistent with expectations.
In actual development, the surrounding behavior of integer overflow should not be relied upon, which is considered a wrong approach. If overflows need to be handled explicitly, you can use the following method families provided by the standard library for integers:
-
wrapping_*
:likewrapping_add
, always perform surround operations; -
checked_*
:likechecked_add
, if overflowing, returnNone
; -
overflowing_*
:likeoverflowing_add
, return a tuple(Result, bool)
,inbool
Indicates whether an overflow occurred; -
saturating_*
:likesaturating_add
, the result will automatically "saturate" to the minimum or maximum value of the corresponding type when overflowing.
2. Floating-Point Types
Rust provides two native floating point types:f32
(32 bits) andf64
(64 bits). Used by defaultf64
, because on modern CPUs,f64
andf32
The speed is almost the same, but the accuracy is higher. All floating point types are signed numbers.
fn main() { let x = 2.0; // f64 let y: f32 = 3.0; // f32 println!("x = {}, y = {}", x, y); }
Rust's floating point numbers follow the IEEE-754 standard.
3. Numeric Operations
Rust supports common numerical operations: addition, subtraction, multiplication, division and balance. It should be noted thatInteger divisionWill round in the zero direction (truncate the decimal part). Example:
fn main() { // Addition let sum = 5 + 10; // Subtraction let difference = 95.5 - 4.3; // Multiplication let product = 4 * 30; // Division let quotient = 56.7 / 32.2; // Take the rest let remainder = 43 % 5; println!("sum = {}", sum); println!("difference = {}", difference); println!("product = {}", product); println!("quotient = {}", quotient); println!("remainder = {}", remainder); }
If you need to view all operators provided by Rust, you can refer toAppendix B。
4. Boolean Type
Boolean type (bool
) There are only two possible values:true
andfalse
. It takes up 1 byte in size. For example:
fn main() { let t = true; let f: bool = false; println!("t = {}, f = {}", t, f); }
Booleans are often used for conditional judgments (e.g.if
Expression), which will be described in detail later in the "Control Flow" section.
5. Character Type
Rust'schar
Types are the most basic letter types, wrapped in single quotes, and support Unicode Scalar Value. This means it can represent more characters than ASCII, such as accented Latin characters, Chinese, Japanese, Korean, emoji, zero-width spaces, etc. For example:
fn main() { let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻'; println!("{}, {}, {}", c, z, heart_eyed_cat); }
Rust'schar
The type occupies 4 bytes, corresponding to the Unicode Scalar Value range:U+0000
~ U+D7FF
andU+E000
~ U+10FFFF
. It should be noted that Unicode's concept of "character" may not be exactly consistent with the "character" in people's intuition. For details, please refer to the following sectionChapter 8 Discussion on Strings。
2. Compound Types
A composite type can combine multiple values into one type. Rust provides two native composite types:Tuples(tuple) andArray(array)。
1. Tuple Type
Tuples can combine multiple values of different types into a compound type with fixed lengths and cannot be increased or shortened. Use brackets()
Contain and separate different values with commas. For example:
fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1); println!("tup = {:?}", tup); }
1.1 Destructuring tuples
To get individual values in tuples, you can deconstruct using pattern matching:
fn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; println!("y = {}", y); }
After execution,y
The value is6.4
. heretup
Being "disassembled" intox
, y
, z
The process of three variables is calledDeconstruction。
1.2 Accessing tuples using indexes
You can also directly use dot numbers and indexes to access the specified elements of a tuple:
fn main() { let x: (i32, f64, u8) = (500, 6.4, 1); let five_hundred = x.0; let six_point_four = x.1; let one = x.2; println!("{}, {}, {}", five_hundred, six_point_four, one); }
It should be noted that the index starts at 0.
1.3 Unit Type
If the tuple does not contain any elements, it is calledUnit Tuples(unit). It's written()
, represents a null value or a null return type. If an expression does not return any other value, the unit tuple is returned by default.
2. Array Type
Arrays are also a way to combine multiple values, but they have two main differences from tuples:
- In the arrayAll elements have the same type;
- ArrayFixed length, Once declared, the length cannot be changed.
For example:
fn main() { let a = [1, 2, 3, 4, 5]; println!("{:?}", a); }
Arrays are usually stored inStackon (stack) instead of heap, this isChapter 4Will explain in detail. If a scalable sequence is required, use thevector(vector,Vec<T>
). If you need a fixed-length sequence, arrays are perfect. For example, the month name:
let months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ];
2.1 Type annotation for arrays
When declaring an array type, you need to write element type, semicolon, and number of elements in square brackets:
let a: [i32; 5] = [1, 2, 3, 4, 5];
herei32
is the type of each element,5
Represents the length of the array.
2.2 Initialize to the same element
If you want all elements of the array to be the same, you can use the following syntax:
let a = [3; 5]; // Equivalent to let a = [3, 3, 3, 3, 3];
2.3 Accessing array elements
You can use indexes to access array elements:
fn main() { let a = [1, 2, 3, 4, 5]; let first = a[0]; let second = a[1]; println!("first = {}, second = {}", first, second); }
2.4 Out-of-bounds access and runtime errors
If the index exceeds the length of the array, Rust will beRuntimeCheck for an error and panic:
fn main() { let a = [1, 2, 3, 4, 5]; println!("Please enter an array index."); let mut index = String::new(); std::io::stdin() .read_line(&mut index) .expect("Read failed"); let index: usize = index .trim() .parse() .expect("The index of the input is not a number"); let element = a[index]; println!("The element you choose is:{}", element); }
If you typed out[0..4]
The index of the range, such as 10, will trigger a panic, showing something like:
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/:19:19
The program exits and will not execute subsequentprintln!
. This is the embodiment of Rust's memory security: many low-level languages may access illegal memory addresses when indexed out of bounds, causing unpredictable consequences, while Rust detects and exits directly at runtime to ensure security.
summary
In this article, we introduce two of the most commonly used subsets of data types in Rust:Scalar TypeandCompound type. Scalar types include integers, floating point numbers, booleans, and characters, each with different representations and ranges; composite types include tuples and arrays, which can be used to combine multiple values into one type, and differ in terms of whether the length is mutable and type consistency.
-
Scalar Type:
-
Integer:like
i32
,u32
,i8
,u8
etc. Different word lengths and signed/unsigned selection; -
Floating point number:
f32
andf64
, defaultf64
; -
Boole:
bool
, onlytrue
andfalse
; -
character:
char
, accounting for 4 bytes, can represent Unicode Scalar Value.
-
Integer:like
-
Compound type:
- Tuples: Can contain multiple types, fixed length; can be accessed using deconstruction or indexing;
- Array: A collection of elements of the same type, with a fixed length and stored on the stack.
For novices, when encountering situations where types cannot be automatically inferred, type annotations are required, especially when usingparse
Or other scenarios where specific numerical types need to be specified. As practice deepens, the various security check mechanisms provided by Rust (such as integer overflow checking, array out-of-bounds checking, etc.) will give you more confidence and a sense of security, and you also need to be familiar with these mechanisms to write efficient and secure code.
In subsequent chapters, we will continue to deepen the characteristics of Rust, including ownership, references and slices, collection types (vectors, strings, hash mappings), and error handling. We hope that you can continue to explore and learn Rust.
References and thanks
- The Rust Programming Language - By Steve Klabnik and Carol Nichols, CC BY 4.0
- Part of this article is based on its translation and rewriting. If you need more details, please read the official documentation.
This is the end of this article about the detailed explanation of Rust data types. For more related Rust data types, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!