A Simple Guide to Rust Data Types
This simple guide on Rust Data Types clears all your doubts while you can focus on building your next big application using the Rust programming language.
Join the DZone community and get the full member experience.
Join For FreeEvery programming language depends on data types. The same goes for the Rust programming language. Rust data types cover the various needs of storing data. Since Rust is a statically typed language, data types become even more important. The Rust compiler uses the data types at the compilation time to make sure that the program is correct.
Rust data types are divided into two major types: scalar and compound. We will look at both types in detail.
The Rust Scalar Data Types
Scalar types store a single value. They are also known as primitive data types in other programming languages.
The Integer Type
An integer is the most common scalar data type. It is basically a number without any fractional component. We can declare an integer as follows:
fn main() {
let counter: u32 = 5;
}
In Rust, integers can be unsigned or signed. Also, the integer data type has a few variants. We can use the appropriate variant depending on our data requirements.
The below table of Rust integer data types describes the available variants:
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 |
The integer variants differ on the basis of size. Signed and unsigned signifies whether the number can be negative or only positive; or in other words, if the number has to support a sign or not. When we expect the number to be always positive, there is no need to have a signed integer.
The signed variants can store numbers from -(2n-1)
to 2n-1-1
inclusive. Basically, n is the number of bits. So, for example, the 8-bit signed integer can store numbers from -128 to 127. Also, the unsigned 8-bit variant stores numbers from 0 to 255.
The isize
and usize
types depend on the architecture of the computer. On a 64-bit machine, this will mean an integer with a size of 64-bit. On a 32-bit machine, this will amount to 32 bits.
Floating-Point Types
Rust also has two primitive types for floating-point numbers. Basically, these are numbers with decimal points.
Rust has two floating-point types. They are f32
and f64
. Basically, f32
is for 32 bits and f64
is for 64 bits. On modern CPUs, the default type is f64
. Also, all floating-point numbers are signed.
We can declare a floating-point number as follows:
fn main() {
let price = 9.99; //This will be f64
let temperature: f32 = 32.6; //This will be f32
}
The Boolean Type
Boolean is the third important scalar type in Rust. Basically, boolean has two possible values: true
and false
. Also, booleans are one byte in size.
See the below example on how to declare a boolean.
fn main() {
let isAvailable: bool = false;
}
Booleans are extremely important in writing conditional statements.
The Character Type
Rust also has a primitive alphabetic type. This is known as the char
type. Basically, this type is used to declare character values.
fn main() {
let option = 'A';
}
The char
literals are specified using single quotes. This is different from string literals where we use double-quotes.
Also, the char
type in Rust is 4 bytes in size. It also represents Unicode Scalar Value. In other words, a char
in Rust can represent a lot more than just ASCII.
The Rust Compound Data Types
With scalar types out of the way, we can now look at compound types in Rust. Basically, compound types are used to group multiple values into one type.
There are a couple of primitive compound types:
The Tuple Data Type
A tuple is a way of grouping together a number of values with a variety of types into one compound type. However, tuples have fixed lengths: once declared, they cannot grow or shrink in size.
Let us see how we can create a tuple.
fn main() {
let demoTuple: (u32, f64, u16) = (525, 9.99, 2);
}
Basically, to create a tuple, we need to provide a comma-separated list of values within parentheses. Each position in the tuple has an individual type. Also, each type can be different. The type annotations shown in the above snippet are optional.
To access elements from within the tuple, we can use pattern matching to de-structure the tuple value.
See the below example:
fn main() {
let demoTuple: (u32, f64, u16) = (525, 9.99, 2);
let (a, b, c) = demoTuple;
}
Here, the variables a
, b
and c
will have the respective values from the tuple.
Also, we can access a tuple element directly by using the period followed by the index of the value. See the below example:
fn main() {
let demoTuple: (u32, f64, u16) = (525, 9.99, 2);
let firstNumber = demoTuple.0;
let secondNumber = demoTuple.1;
}
We can also have a tuple without any value. This is a special type and is written as ()
. We also call it the unit type and the value is called unit value.
The Array Data Type
The next way to have a collection of multiple values is by using an array. However, arrays differ from tuples in one fundamental manner. In arrays, every element must have the same data type.
We can declare an array in Rust as below:
fn main() {
let numbers = [4, 8, 7, 3, 2];
}
Arrays in Rust have a fixed length. Also, when we declare an array, the memory is allocated on the stack rather than the heap.
We should use arrays when we know the number of elements in the array will not change over time.
Array declaration can also contain the data type and the number of elements:
let numbers: [u32; 5] = [4, 8, 7, 3, 2];
We can also initialize an array with the same value for each element using the below syntax:
let numbers: [1; 5];
This will create an array of 5 elements where each element will have the value 1 initially.
Since array sizes are known at compile-time, Rust allocates a single chunk of memory on the stack. This allows us to access elements of the array using indexing.
For example, to access the second element in the below array, we have to use the index 1.
fn main() {
let numbers = [4, 8, 7, 3, 2];
let second = numbers[1];
}
The index starts from 0.
Conclusion
Rust data types cover all the use-cases a typical application may need to function properly. We have scalar data types to store individual values and compound data types to store a group of values. However, an important concept to working with data in Rust is Ownership and Borrowing.
If you have any comments or queries about this post, please feel free to mention them in the comments section below.
Published at DZone with permission of Saurabh Dashora. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments