bool
true or false
let a = true;
let b: bool = false;
// βοΈ no TRUE, FALSE, 1, 0
bool is a single byte(8 bits) in size.
char
A single Unicode scalar value
let a = 'x';
let b: char = 'π';
// βοΈ no "x", only single quotes
Because of Unicode support, char is not a single byte, but four(32 bits).
i8, i16, i32, i64, i128
8, 16, 32, 64 and 128 bit fixed sized signed(+/-) integer types
| DATA TYPE | MIN | MAX |
|---|---|---|
| i8 | -128 | 127 |
| i16 | -32768 | 32767 |
| i32 | -2147483648 | 2147483647 |
| i64 | -9223372036854775808 | 9223372036854775807 |
| i128 | -170141183460469231731687303715884105728 | 170141183460469231731687303715884105727 |
π‘ The min and max values are based on the following equation; from -(2βΏβ»ΒΉ) to 2βΏβ»ΒΉ-1. You can use MIN and MAX constants to find min and max of each integer type. ex.i8::MIN;
let a = 10; // βοΈ The default integer type in Rust is i32
let b: i8 = -128;
u8, u16, u32, u64, u128
8, 16, 32, 64 and 128 bit fixed sized unsigned(0/+) integer types
| DATA TYPE | MIN | MAX |
|---|---|---|
| u8 | 0 | 255 |
| u16 | 0 | 65535 |
| u32 | 0 | 4294967295 |
| u64 | 0 | 18446744073709551615 |
| u128 | 0 | 340282366920938463463374607431768211455 |
π‘ The min and max values are based on the following equation; from 0 to 2βΏ-1. Same way you can use MIN and MAX constants to find min and max of each integer type. ex.u8::MAX
isize, usize
Pointer sized signed and unsigned integer types
The actual bit size depends on the computer architecture you are compiling your program for. By default, the sizes are equal to 32 bits on 32-bit platforms and 64 bits on 64-bit platforms. You can use MIN and MAX constants to find min and max of each integer type. ex.isize::MAX.
π Search more about cross-compiling and supported tiers of Rust programs.
f32, f64
32 and 64 bit sized floating point numbers(numbers with decimal points)
Rust follows IEEE Standard for Binary Floating-Point Arithmetic. The f32 type is similar to float(Single precision) in other languages, while f64 is similar to double(Double precision) in other languages.
let a = 1.5; // βοΈ The default float type in Rust is f64
let b: f64 = 2.0;
π‘ Should avoid using f32, unless you need to reduce memory consumption badly or if you are doing low-level optimization, when targeted hardware does not support for double-precision or when single-precision is faster than double-precision on it.
Array
Fixed size list of elements of same data type
let a = [1, 2, 3];
let b: [i32; 3] = [1, 2, 3]; // with the data type π‘ [Type; NO of elements]
// π‘let b: [i32; 3] = [1, 2]; // Compiling error : mismatched types : expected an array with a size of 3, found one with a size of 2
let c: [i32; 0] = []; // An empty array
// Accessing and changing elements
let mut a: [i32; 3] = [1, 2, 3];
a[0] = 2;
a[1] = 4;
a[2] = 6;
// Printing with debug and pretty-print debug specifiers
println!("{a:?}"); // [2, 4, 6]
println!("{a:#?}");
// [
// 2,
// 4,
// 6,
// ]
let a = [0; 5]; // [0, 0, 0, 0, 0]
let b = ["x"; 5]; // ["x", "x", "x", "x", "x"]
βοΈ Arrays are immutable by default and even with mut, its element count cannot be changed.
π If you are looking for a dynamic/ growable array, you can use vectors. Vectors can contain any type of elements but all elements must be in the same data type.
Tuple
Fixed size ordered list of elements of different(or same) data types
let a = (1, 1.5, true, 'a');
let b: (i32, f64, bool, char) = (1, 1.5, true, 'a'); // With the data types
let c = (0,); // single-element tuple
// Accessing and changing elements
let mut a = (1, 1.5);
a.0 = 2;
a.1 = 3.0;
// Printing with debug and pretty-print debug specifiers
println!("{a:?}"); // (2, 3.0)
println!("{a:#?}");
// (
// 2,
// 3.0,
// )
// Destructuring
let a = (1, 1.5);
let (b, c) = a; // b = 1, c = 1.5
let a = (1, 1.5, true, 'a');
let (b, _, _, c) = a; // b = 1, c = 'a'
let (b, .., c) = a; // b = 1, c = 'a'
let a = (1, 2, 3, true, false, 'a', 'b', 'c');
let (b, ..) = a; // b = 1
let (.., c) = a; // c = 'c'
let (e, f, g, .., h, i, j) = a; // 1 2 3 'a' 'b' 'c'
// Nesting
let a = (1, 1.5);
let b = (a, (2, 4), 6); // ((1, 1.5), (2, 4), 6)
βοΈ Tuples are also immutable by default and even with mut, its element count cannot be changed. Also, if you want to change an elementβs value, the new value should have the same data type of previous value.
Slice
Dynamically-sized reference to another data structure
Imagine you want to get/ pass a part of an array or any other data structure. Instead of copying it to another array (or same data structure), Rust allows for creating a view/ reference to access only that part of the data. This view/ reference can be mutable or immutable.
let a: [i32; 4] = [1, 2, 3, 4]; // Parent Array
// Slice whole array
let b: &[i32] = &a; // data type is optional
let c = &a[0..4]; // From 0th position to 4th(excluding)
let d = &a[..]; // high or low bounds are optional
// Slicing part of the array
let e = &a[1..3]; // [2, 3]
let f = &a[1..]; // [2, 3, 4]
let g = &a[..3]; // [1, 2, 3]
// directly creating a slice
let h = &[1, 2];
let h: &[i32] = &[1, 2]; // with the datatype
str
Unsized UTF-8 sequence of Unicode string slices
let a = "Hello, world."; // a: &'static str
let b: &str = "γγγ«γ‘γ―, δΈη!";
str is a UTF-8 sequence of Unicode string slice; It’s unsized. You canβt create a variable of type str directly because Rust needs to know sizes at compile time. So, it must always be used behind a pointer β like &str, Box<str>, or Rc<str>.
&str is a fat pointer (a pointer to str and a length). So, it’s a sized reference that carries both a pointer and a length.
&'static str is an &str that is statically allocated directly to the read-only data segment in the program binary. The 'static lifetime means it lives for the entire program duration.
π― String
- π‘ String: capital “S” as it’s a struct.
Stringtype is a growable, heap-allocated, UTF-8 encoded string. It is a sized type.- String is the most common string type. In general, you should use
Stringwhen you needownership, and&strwhen you just need to borrow a string.- Can be generated from a
&strtype, viato_string(),to_owned()orString::from()methods. Withas_str()method, aStringtype can be converted to a&strtype.let s: &str = "Hello"; // &str let a = s.to_string(); // String let b = s.to_owned(); // String let c = String::from(s); // Stringlet a = String::from("Hello"); // String let b = a.as_str(); // &str
Function
p1 is a function pointer to plus_one() in the following code.
fn main() {
let p1 = plus_one; // Without type declarations
let a = p1(5); // 6
let p1: fn(i32) -> i32 = plus_one; // With the type declarations
let b = p1(5); // 6
}
fn plus_one(i: i32) -> i32 {
i + 1
}
Type Inference
The Rust compiler can infer the data type when we don’t explicitly specify it. For example, in Rust, the default integer type is i32 and the default float type is f64. However, the Rust compiler checks the usage and determines the best data type based on the context.
let a = 10; // type inference: i32
let b = 3.14; // type inference: f64
let c = true; // type inference: bool
fn main() {
let a = 8; // type inference: i8
println!("{}", plus_one(a));
}
fn plus_one(i: i8) -> i8 {
i + 1
}
Default Byte Sizes of Data Types
We can check the default byte sizes of data types via std::mem::size_of. For example: size_of::<bool>(), size_of::<f32>(), size_of::<&str>(), etc.
| Data type | bytes | bits |
|---|---|---|
| bool | 1 | 8 |
| char | 4 | 32 |
| i8 i16 i32 i64 i128 | 1, 2, 4, 8, 16 | 8, 16, 32, 64, 128 |
| u8 u16 u32 u64 u128 | 1, 2, 4, 8, 16 | 8, 16, 32, 64, 128 |
| isize usize | 8, 8 | 64, 64 |
| f32 f64 | 4, 8 | 32, 64 |
| &str | 16 | 128 |
π¨βπ« Before going to the next…
Other than adding the type annotations to the variables, for numeric types, we can append the data type directly to the value as the suffix. Also, to improve the readability of long numbers, we can use
_as a divider.let a = 5i8; // Equals to `let a: i8 = 5;` let b = 100_000_000; // Equals to `let b = 100000000;` // π‘ The placements of _s are not strict. ex. 10000_0000 is also valid. let pi = 3.141_592_653_59_f64; // Equals to `let pi: f64 = 3.14159265359` // π‘ make sure group digits consistently by underscores avoid warnings const PI: f64 = 3.141_592_653_59; // In the constants and statics, the data type must be annotated in the beginning.