rust programming

Rust Fundamentals: Core Language Concepts

Introduction

Every programming language shares common concepts like variables, basic types, functions, comments, and control flow. Understanding these fundamentals in Rust will accelerate your learning journey and provide a solid foundation for more advanced concepts.

Variables and Mutability

Rust is a strongly-typed language with type inference capabilities. This feature shouldn't be confused with dynamic typing - Rust's type system is strict but smart enough to figure out types when they're obvious.

Variable Declaration

By default, variables in Rust are immutable. Here's how to declare variables:

let a = 123;        // Immutable variable
let mut b = 10;     // Mutable variable

Understanding Immutability

Rust's immutability by default is a design choice that promotes safer concurrent code. Consider this example:

let a = 123;

// The following operations are not allowed:
// a = "abc";    // Error: Type mismatch
// a = 4.56;     // Error: Precision loss
// a = 456;      // Error: Cannot modify immutable variable

Variable Shadowing

Rust allows variable shadowing, which is different from mutation:

let x = 5;
let x = x + 1;    // Creates a new variable, shadows the previous 'x'
let x = x * 2;    // Creates another new variable
println!("Value: {}", x);    // Outputs: Value: 12

Type Annotations

While Rust can infer types, explicit type annotations are sometimes useful:

let a: u64 = 123;    // Explicitly declare as unsigned 64-bit integer
let b = 123;         // Inferred as i32 (signed 32-bit integer)

Data Types

Scalar Types

  1. Integers

    let signed: i32 = -42;        // 32-bit signed
    let unsigned: u32 = 42;       // 32-bit unsigned
    let big_number: i64 = 1000;   // 64-bit signed
    
  2. Floating-Point

    let float: f64 = 3.14;        // 64-bit float
    let precise: f32 = 3.14159;   // 32-bit float
    
  3. Boolean

    let is_active: bool = true;
    let is_ready = false;         // Type inference
    
  4. Character

    let letter: char = 'A';       // Single quotes for char
    let emoji = '😀';             // Supports Unicode
    

Control Flow

If Expressions

let number = 7;
if number < 5 {
    println!("Less than 5");
} else if number > 10 {
    println!("Greater than 10");
} else {
    println!("Between 5 and 10");
}

Loops

  1. loop - Infinite Loop

    let mut counter = 0;
    loop {
        counter += 1;
        if counter == 10 {
            break;
        }
    }
    
  2. while Loop

    let mut number = 3;
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    
  3. for Loop

    for number in 1..4 {
        println!("{}!", number);
    }
    

Ownership System

Rust's ownership system is its most unique feature, managing memory safety without garbage collection.

Key Concepts

  1. Ownership Rules

    • Each value has exactly one owner
    • Only one owner at a time
    • Value is dropped when owner goes out of scope
  2. Example of Ownership Transfer

    let s1 = String::from("hello");
    let s2 = s1;    // s1's ownership moves to s2
    // println!("{}", s1);    // Error: s1 is no longer valid
    
  3. Borrowing with References

    fn main() {
        let s = String::from("hello");
        let len = calculate_length(&s);    // Borrow s
        println!("Length of '{}' is {}.", s, len);
    }
    
    fn calculate_length(s: &String) -> usize {
        s.len()
    }
    

Error Handling

Rust uses two main types for error handling:

Result&lt;T, E&gt;

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Division by zero"))
    } else {
        Ok(a / b)
    }
}

Option&lt;T&gt;

fn get_element(index: usize, vec: &Vec<i32>) -> Option<i32> {
    if index < vec.len() {
        Some(vec[index])
    } else {
        None
    }
}

Best Practices

  1. Use Immutability by Default

    • Only make variables mutable when necessary
    • Helps prevent bugs and makes code easier to reason about
  2. Type Annotations

    • Use type annotations when type inference isn't obvious
    • Makes code more readable and self-documenting
  3. Error Handling

    • Prefer Result over panic!
    • Use Option for values that might not exist
  4. Variable Naming

    • Use descriptive names
    • Follow Rust's snake_case convention

Common Pitfalls to Avoid

  1. Ownership Confusion

    // Wrong
    let s1 = String::from("hello");
    let s2 = s1;
    println!("{}", s1);    // Error
    
    // Correct
    let s1 = String::from("hello");
    let s2 = s1.clone();   // Explicitly clone if you need both
    println!("{}", s1);    // OK
    
  2. Mutability vs. Shadowing

    // Mutability
    let mut x = 5;
    x = 6;    // Changes value, same variable
    
    // Shadowing
    let x = 5;
    let x = x + 1;    // Creates new variable
    

Next Steps

After mastering these fundamentals, you'll be ready to explore:

  • Advanced ownership concepts
  • Structs and enums
  • Pattern matching
  • Modules and packages
  • Concurrency

Remember: Rust's strict rules might feel constraining at first, but they help prevent common programming errors and make your code more reliable.