Rust

#13
TIOBE#18
PYPL#8
GitHub#25
RedMonk#19
IEEESpectrum#11
JetBrains#14
Programming LanguageSystems ProgrammingMemory SafetyHigh PerformanceWebAssemblyConcurrency

Programming Language

Rust

Overview

Rust is a systems programming language that combines memory safety with high performance.

Details

Rust was initially developed by Graydon Hoare at Mozilla starting in 2010 as a systems programming language. Its main feature is the ownership system that guarantees memory safety without using garbage collection. It maintains high performance like C/C++ while preventing issues such as memory leaks, buffer overflows, and data races at compile time. It's being adopted in areas where performance is critical, such as web browser engines, operating systems, WebAssembly, cryptocurrencies, and game engines. Recently, it's also beginning to be adopted in Linux kernel development.

Code Examples

Hello World

// Basic output
fn main() {
    println!("Hello, World!");
    
    // Output using variables
    let message = "Hello, Rust!";
    println!("{}", message);
    
    // Output using format strings
    let name = "John";
    let age = 25;
    println!("My name is {} and I am {} years old.", name, age);
}

Variables and Ownership

fn main() {
    // Immutable variables (default)
    let name = "John";
    let age = 25;
    
    // Mutable variables
    let mut score = 85;
    score = 90; // Can be changed
    
    // Type annotations
    let height: f64 = 175.5;
    let is_active: bool = true;
    
    // Ownership move
    let s1 = String::from("Hello");
    let s2 = s1; // Ownership of s1 moves to s2
    // println!("{}", s1); // Error: s1 cannot be used
    println!("{}", s2);
    
    // Clone (copy)
    let s3 = String::from("World");
    let s4 = s3.clone(); // Clone s3
    println!("{} {}", s3, s4); // Both can be used
    
    // Borrowing (references)
    let s5 = String::from("Rust");
    let len = calculate_length(&s5); // Borrow
    println!("The length of '{}' is {}.", s5, len); // s5 can still be used
}

fn calculate_length(s: &String) -> usize {
    s.len() // Borrowing doesn't move ownership
}

Data Types and Collections

fn main() {
    // Basic types
    let integer: i32 = 42;
    let float: f64 = 3.14;
    let character: char = '🦀';
    let boolean: bool = true;
    
    // Tuples
    let person: (String, i32, f64) = (String::from("Tanaka"), 30, 175.5);
    let (name, age, height) = person; // Destructure
    println!("Name: {}, Age: {}, Height: {}cm", name, age, height);
    
    // Arrays (fixed length)
    let numbers: [i32; 5] = [1, 2, 3, 4, 5];
    let first = numbers[0];
    
    // Vectors (variable length)
    let mut fruits = vec!["apple", "banana", "orange"];
    fruits.push("grape"); // Add element
    
    // HashMap
    use std::collections::HashMap;
    let mut scores = HashMap::new();
    scores.insert(String::from("Math"), 85);
    scores.insert(String::from("English"), 92);
    scores.insert(String::from("Science"), 78);
    
    // Pattern matching for access
    match scores.get("Math") {
        Some(score) => println!("Math score: {}", score),
        None => println!("Math score not found"),
    }
    
    println!("Fruits: {:?}", fruits);
    println!("Scores: {:?}", scores);
}

Structs and Methods

// Struct definition
#[derive(Debug)] // Enable debug output
struct Person {
    name: String,
    age: u32,
    email: String,
}

// Method implementation
impl Person {
    // Associated function (constructor-like)
    fn new(name: String, age: u32, email: String) -> Person {
        Person { name, age, email }
    }
    
    // Method (using &self)
    fn get_info(&self) -> String {
        format!("{} ({} years old) - {}", self.name, self.age, self.email)
    }
    
    // Mutable method (using &mut self)
    fn set_age(&mut self, new_age: u32) {
        self.age = new_age;
    }
    
    // Consuming method (using self)
    fn into_email(self) -> String {
        self.email
    }
}

// Tuple struct
struct Point(f64, f64);

impl Point {
    fn distance_from_origin(&self) -> f64 {
        (self.0 * self.0 + self.1 * self.1).sqrt()
    }
}

fn main() {
    // Create struct instance
    let mut person = Person::new(
        String::from("John Doe"),
        25,
        String::from("[email protected]"),
    );
    
    println!("{}", person.get_info());
    
    // Call mutable method
    person.set_age(26);
    println!("Updated: {}", person.get_info());
    
    // Tuple struct
    let point = Point(3.0, 4.0);
    println!("Distance from origin: {:.2}", point.distance_from_origin());
    
    // Debug output
    println!("Debug: {:?}", person);
}

Enums and Pattern Matching

// Enum definition
#[derive(Debug)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

// Option type usage example
fn find_word(text: &str, word: &str) -> Option<usize> {
    text.find(word)
}

// Result type usage example
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Cannot divide by zero"))
    } else {
        Ok(a / b)
    }
}

impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("Quit the program"),
            Message::Move { x, y } => println!("Move to ({}, {})", x, y),
            Message::Write(text) => println!("Message: {}", text),
            Message::ChangeColor(r, g, b) => {
                println!("Change color to RGB({}, {}, {})", r, g, b)
            }
        }
    }
}

fn main() {
    // Using enums
    let messages = vec![
        Message::Move { x: 10, y: 20 },
        Message::Write(String::from("Hello, Rust!")),
        Message::ChangeColor(255, 0, 0),
        Message::Quit,
    ];
    
    for message in messages {
        message.call();
    }
    
    // Using Option type
    let text = "Hello, Rust programming!";
    match find_word(text, "Rust") {
        Some(index) => println!("'Rust' found at position {}", index),
        None => println!("'Rust' not found"),
    }
    
    // Concise pattern matching with if let
    if let Some(index) = find_word(text, "programming") {
        println!("'programming' found at position {}", index);
    }
    
    // Using Result type
    match divide(10.0, 3.0) {
        Ok(result) => println!("10.0 ÷ 3.0 = {:.2}", result),
        Err(error) => println!("Error: {}", error),
    }
    
    // Concise error handling with ? operator
    if let Err(e) = try_division() {
        println!("Calculation error: {}", e);
    }
}

fn try_division() -> Result<(), String> {
    let result = divide(5.0, 0.0)?; // Early return on error
    println!("Result: {}", result);
    Ok(())
}

Traits (Interfaces)

// Trait definition
trait Summary {
    fn summarize(&self) -> String;
    
    // Default implementation
    fn announce(&self) {
        println!("Summary: {}", self.summarize());
    }
}

// Struct definitions
struct NewsArticle {
    headline: String,
    location: String,
    author: String,
    content: String,
}

struct Tweet {
    username: String,
    content: String,
    reply: bool,
    retweet: bool,
}

// Trait implementation
impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{} by {} ({})", self.headline, self.author, self.location)
    }
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

// Function using trait bounds
fn notify(item: &impl Summary) {
    println!("Breaking news: {}", item.summarize());
}

// Multiple trait bounds
use std::fmt::Display;

fn some_function<T>(item: &T) -> String
where
    T: Summary + Display,
{
    format!("Display: {} | Summary: {}", item, item.summarize())
}

// Implementing standard library traits
impl Display for Tweet {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Tweet by {}: {}", self.username, self.content)
    }
}

fn main() {
    let article = NewsArticle {
        headline: String::from("Rust programming language gaining popularity"),
        location: String::from("Tokyo"),
        author: String::from("John Doe"),
        content: String::from("Rust adoption is increasing..."),
    };
    
    let tweet = Tweet {
        username: String::from("rust_lover"),
        content: String::from("Programming in Rust!"),
        reply: false,
        retweet: false,
    };
    
    // Call trait methods
    article.announce();
    tweet.announce();
    
    // Call function with trait bounds
    notify(&article);
    notify(&tweet);
    
    // Multiple trait bounds
    println!("{}", some_function(&tweet));
}

Lifetimes

// Lifetime annotations
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

// Lifetimes in structs
struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
    
    // Lifetime elision rules automatically infer 'a
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

fn main() {
    let string1 = String::from("long string is long");
    
    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
    
    // Using lifetimes in structs
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let excerpt = ImportantExcerpt {
        part: first_sentence,
    };
    
    println!("Important excerpt: {}", excerpt.part);
    let announcement = "A new chapter begins";
    println!("Return value: {}", excerpt.announce_and_return_part(announcement));
}

Concurrent Programming

use std::thread;
use std::time::Duration;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};

fn main() {
    // Basic threading
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    
    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
    
    handle.join().unwrap(); // Wait for thread completion
    
    // Channel communication
    let (tx, rx) = mpsc::channel();
    
    thread::spawn(move || {
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];
        
        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
    
    for received in rx {
        println!("Got: {}", received);
    }
    
    // Shared state (Mutex)
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("Result: {}", *counter.lock().unwrap());
}

Error Handling and Testing

// Custom error type
use std::fmt;
use std::error::Error;

#[derive(Debug)]
struct CustomError {
    message: String,
}

impl fmt::Display for CustomError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Custom error: {}", self.message)
    }
}

impl Error for CustomError {}

// Function that returns error
fn divide_numbers(a: f64, b: f64) -> Result<f64, Box<dyn Error>> {
    if b == 0.0 {
        Err(Box::new(CustomError {
            message: String::from("Cannot divide by zero"),
        }))
    } else {
        Ok(a / b)
    }
}

// Test function
pub fn add_two(a: i32) -> i32 {
    a + 2
}

fn main() {
    // Error handling examples
    match divide_numbers(10.0, 2.0) {
        Ok(result) => println!("10 ÷ 2 = {}", result),
        Err(e) => println!("Error: {}", e),
    }
    
    match divide_numbers(10.0, 0.0) {
        Ok(result) => println!("10 ÷ 0 = {}", result),
        Err(e) => println!("Error: {}", e),
    }
    
    // Panic example (program crashes)
    // panic!("This is a panic!");
}

// Test module
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn it_adds_two() {
        assert_eq!(4, add_two(2));
    }
    
    #[test]
    fn test_divide_numbers() {
        assert_eq!(divide_numbers(10.0, 2.0).unwrap(), 5.0);
        assert!(divide_numbers(10.0, 0.0).is_err());
    }
    
    #[test]
    #[should_panic]
    fn test_panic() {
        panic!("This test should panic");
    }
}

Versions

Version Release Date Major Features
Rust 1.81 2024-09 Error trait in core, Lint against ambiguous wide pointer comparisons
Rust 1.80 2024-07 LazyCell and LazyLock, Exclusive ranges in patterns
Rust 1.79 2024-06 Inline const expressions, Bounds in associated type position
Rust 1.78 2024-05 Diagnostic attributes, assert_unsafe_precondition
Rust 1.77 2024-03 C-string literals, Offset of struct fields
Rust 1.76 2024-02 Arc::unwrap_or_clone, Inspection functions on Option and Result
Rust 1.75 2023-12 async fn and return-position impl trait in traits

Reference Links

Official Documentation

Learning Resources

Development Tools