๐Ÿ‘ˆ Back

Getting Started with Rust

A comprehensive guide to learning Rust programming language

By Keith Thomson โ€ข 8 min read โ€ข rust

๐Ÿš€ Getting Started

Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety. In this post, weโ€™ll explore the fundamentals of Rust, why itโ€™s becoming increasingly popular among developers, and walk through practical examples that show how Rust differs from other languages.

๐ŸŒŸ Why Rust?

Rust offers several advantages over traditional systems programming languages like C and C++:

  • Memory Safety: Prevents common bugs like null pointer dereferences and buffer overflows.
  • Performance: Zero-cost abstractions โ€” you donโ€™t pay for features you donโ€™t use.
  • Concurrency: Built-in support for safe, data-race-free concurrent programming.
  • Modern Ecosystem: A strong package manager (cargo), vibrant community, and modern tooling.

๐Ÿ’ก Tip: Rust enforces correctness at compile time, saving you from runtime surprises that are common in C/C++.


๐Ÿ”ค Basic Concepts

Letโ€™s start with the classic Hello, World! program:

fn main() {
    println!("Hello, World!");
}

This simple program demonstrates Rustโ€™s clean syntax and its macro system (notice the ! in println!). Macros in Rust are more powerful than standard functions โ€” they can generate code at compile time.


๐Ÿ“ Variables and Mutability

In Rust, variables are immutable by default. This means once you assign a value, it cannot change unless you explicitly declare it as mutable.

fn main() {
    let x = 5;        // immutable
    let mut y = 10;   // mutable

    println!("x = {}", x);
    println!("y = {}", y);

    y = 15;
    println!("y (after change) = {}", y);
}
  • let creates a variable.
  • mut makes it mutable.
  • Rust encourages immutability to reduce bugs and improve safety.

๐Ÿ”‘ Ownership and Borrowing

Rustโ€™s ownership system is its most unique feature. It enforces memory safety without a garbage collector.

Example: Ownership

fn main() {
    let s1 = String::from("Rust");
    let s2 = s1; // ownership moved from s1 to s2

    // println!("{}", s1); // โŒ Error: s1 is no longer valid
    println!("{}", s2);   // โœ… Works
}
  • Variables own their data.
  • When ownership is transferred (moved), the old variable is invalidated.

Example: Borrowing

fn main() {
    let s = String::from("Borrowing in Rust");
    print_length(&s); // pass reference (borrow)
    println!("s is still valid: {}", s);
}

fn print_length(s: &String) {
    println!("Length: {}", s.len());
}
  • & means โ€œborrow without taking ownership.โ€
  • The original variable remains valid.

๐Ÿ”’ This system prevents dangling pointers and memory leaks at compile time.


๐Ÿ”ง Functions and Control Flow

Rust functions look familiar, but with strong typing and return value rules.

fn main() {
    println!("Sum = {}", add(5, 7));

    let number = 6;
    if number % 2 == 0 {
        println!("Even");
    } else {
        println!("Odd");
    }
}

fn add(a: i32, b: i32) -> i32 {
    a + b  // no semicolon = return value
}
  • Functions must declare parameter and return types.
  • Leaving out the semicolon ; makes it an expression that returns a value.

โš ๏ธ Error Handling

Rust does not have exceptions. Instead, it uses:

  • Result<T, E> for recoverable errors.
  • Option<T> for values that may or may not exist.
use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let file = File::open("data.txt");

    match file {
        Ok(_) => println!("File opened successfully."),
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            println!("File not found, creating one...");
        }
        Err(e) => {
            println!("Error: {:?}", e);
        }
    }
}

This forces you to handle errors explicitly.


๐Ÿงต Concurrency

Rust makes concurrency safe by design. Threads must follow ownership and borrowing rules.

use std::thread;

fn main() {
    let handles: Vec<_> = (1..5).map(|i| {
        thread::spawn(move || {
            println!("Hello from thread {}", i);
        })
    }).collect();

    for handle in handles {
        handle.join().unwrap();
    }
}
  • move transfers ownership into the thread.
  • No data races are possible because Rust enforces safe access at compile time.

๐Ÿ Conclusion

Rust combines the performance of C/C++ with modern safety guarantees and a thriving ecosystem. Its ownership model may take time to learn, but it pays off by eliminating entire classes of bugs.

Whether youโ€™re building:

  • ๐Ÿš€ High-performance applications
  • ๐ŸŒ Web servers with frameworks like Actix or Axum
  • ๐Ÿ“Š Data pipelines and concurrent systems
  • ๐Ÿ”’ Secure, low-level embedded software

Rust is quickly becoming the go-to language for systems programming in the modern era.


๐Ÿ’ก Next Step: Try rewriting a small project youโ€™ve built in Python, Go, or C into Rust โ€” youโ€™ll immediately see how ownership, borrowing, and safety rules shape your design.