Data Types

Data Types in Rust

Rust is a statically typed language, meaning it must know the types of all variables at compile time. Let's explore Rust's data types.

Scalar Types

Scalar types represent single values. Rust has four primary scalar types:

1. Integer Types

Integers are whole numbers without fractional components.

LengthSignedUnsigned
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize
fn main() {
    let a: i32 = 42;        // Signed 32-bit integer
    let b: u64 = 100;       // Unsigned 64-bit integer
    let c: isize = -50;     // Architecture-dependent size
    
    println!("a: {}, b: {}, c: {}", a, b, c);
}

Number literals can use:

fn main() {
    let decimal = 98_222;      // Decimal (with separator)
    let hex = 0xff;            // Hexadecimal
    let octal = 0o77;          // Octal
    let binary = 0b1111_0000;  // Binary
    let byte = b'A';           // Byte (u8 only)
    
    println!("Decimal: {}", decimal);
    println!("Hex: {}", hex);
    println!("Octal: {}", octal);
    println!("Binary: {}", binary);
    println!("Byte: {}", byte);
}

2. Floating-Point Types

Rust has two floating-point types:

fn main() {
    let x = 2.0;      // f64 (default)
    let y: f32 = 3.0; // f32
    
    println!("x: {}, y: {}", x, y);
    
    // Floating-point operations
    let sum = x + y as f64;
    let product = x * y as f64;
    println!("Sum: {}, Product: {}", sum, product);
}

3. Boolean Type

The boolean type has two values: true and false.

fn main() {
    let is_active: bool = true;
    let is_greater = 10 > 5;
    
    println!("is_active: {}", is_active);
    println!("is_greater: {}", is_greater);
}

4. Character Type

The char type represents a single Unicode scalar value:

fn main() {
    let letter = 'A';
    let emoji = '😊';
    let chinese = '中';
    
    println!("Letter: {}", letter);
    println!("Emoji: {}", emoji);
    println!("Chinese: {}", chinese);
}

Compound Types

Compound types can group multiple values into one type.

1. Tuples

Tuples group together values of different types:

fn main() {
    let tup: (i32, f64, char) = (500, 6.4, 'z');
    
    // Destructuring
    let (x, y, z) = tup;
    println!("x: {}, y: {}, z: {}", x, y, z);
    
    // Direct access
    let first = tup.0;
    let second = tup.1;
    let third = tup.2;
    println!("First: {}, Second: {}, Third: {}", first, second, third);
}

2. Arrays

Arrays have a fixed length and all elements must be the same type:

fn main() {
    let numbers = [1, 2, 3, 4, 5];
    
    // With type annotation
    let months: [&str; 12] = [
        "January", "February", "March", "April",
        "May", "June", "July", "August",
        "September", "October", "November", "December"
    ];
    
    // Initialize with same value
    let zeros = [0; 5]; // [0, 0, 0, 0, 0]
    
    // Accessing elements
    let first = numbers[0];
    let third_month = months[2];
    
    println!("First number: {}", first);
    println!("Third month: {}", third_month);
}

Type Aliases

Create new names for existing types:

type Kilometers = i32;

fn main() {
    let distance: Kilometers = 100;
    println!("Distance: {} km", distance);
}

Type Conversion

Implicit vs Explicit

Rust doesn't do implicit type conversion. You must be explicit:

fn main() {
    let x: i32 = 5;
    let y: i64 = 10;
    
    // This won't compile:
    // let sum = x + y;
    
    // Explicit conversion
    let sum = x as i64 + y;
    println!("Sum: {}", sum);
}

Parsing Strings

Convert strings to other types:

fn main() {
    let text = "42";
    let number: i32 = text.parse().expect("Not a valid number");
    
    // Or with turbofish syntax
    let number2 = "123".parse::<i32>().expect("Not a valid number");
    
    println!("Parsed: {} and {}", number, number2);
}

Type Inference

Rust can often infer types:

fn main() {
    let x = 5;        // Inferred as i32
    let y = 2.5;      // Inferred as f64
    
    // Sometimes you need to help
    let numbers = vec![1, 2, 3]; // Vec<i32>
    let empty_vec: Vec<i32> = Vec::new(); // Type required
}

Memory Size

Check the size of types:

use std::mem;

fn main() {
    println!("Size of i32: {} bytes", mem::size_of::<i32>());
    println!("Size of f64: {} bytes", mem::size_of::<f64>());
    println!("Size of char: {} bytes", mem::size_of::<char>());
    println!("Size of bool: {} bytes", mem::size_of::<bool>());
    println!("Size of (i32, i32): {} bytes", mem::size_of::<(i32, i32)>());
}

Practical Examples

Working with Different Types

fn main() {
    // Personal data
    let name = "Alice";
    let age: u8 = 30;
    let height: f32 = 5.6;
    let is_student = false;
    let grade = 'A';
    
    println!("{} is {} years old", name, age);
    println!("Height: {} feet", height);
    println!("Student: {}, Grade: {}", is_student, grade);
}

Type Safety Example

fn main() {
    let mut score = 0;
    
    // This is safe
    score = score + 10;
    
    // This won't compile (type safety)
    // score = score + 0.5; // Error: mismatched types
    
    // Must be explicit about conversion
    let final_score = score as f64 + 0.5;
    println!("Final score: {}", final_score);
}