Rust Programming Language Study Notes

Rust Learning Journey

Day 1 (2021/05/27)

Explored constants, variables, data types, control flow, and ownership.

  • char occupies 4 bytes, equivalent to a Unicode scalar value
  • Control flow expressions don't require parentheses
  • Tuples in Rust closely resemble C++ tuple usage

// clang++ test.cpp -std=c++11 && ./a.out
#include <iostream>
#include <string>
#include <tuple>

int main() {
    std::tuple<int, std::string> data = std::make_tuple(42, "example");
    std::cout << std::get<0>(data) << " " << std::get<1>(data) << std::endl;
    
    std::string text;
    int number;
    std::tie(number, text) = std::make_tuple(100, "sample-data");
    std::cout << number << " " << text << std::endl;
}

  • Ownership:
    • Clear and strict rules - resources are released at scope end
    • Assignment transfers ownership, invalidating the previous value (except for primitive types)
    • Multiple mutable references in same scope are prohibited, reducing race conditions
    • Cannot have mutable and immutable references simultaneously
  • Slices: Similar to C++'s string_view or Go's slices for function parameters
  • Structs: Usage similar to C++
    • Method definitions equivalent to C++
    • Associated functions similar to C++ static methods
    • #[derive(Debug)] enables printing entire structure
Exercises
Temperature Conversion

fn celsius_to_fahrenheit(celsius: f32) -> f32 {
    1.8 * celsius + 32.0
}

fn fahrenheit_to_celsius(fahrenheit: f32) -> f32 {
    (fahrenheit - 32.0) / 1.8
}

Fibonacci Sequence

fn compute_fibonacci(n: u32) -> u32 {
    match n {
        0 => 0,
        1 | 2 => 1,
        _ => compute_fibonacci(n - 1) + compute_fibonacci(n - 2),
    }
}


Day 2 (2021/05/28)

Enumerations

Simple enums work like C++:


enum Color { Red, Green, Blue }

Rust allows variants to hold different data types:


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

fn process_message(msg: Message) {
    println!("{:?}", msg);
}

fn main() {
    process_message(Message::Quit);
    process_message(Message::Move { x: 10, y: 20 });
    process_message(Message::Write(String::from("test-message")));
    process_message(Message::ChangeColor(255, 0, 0));
}

Option<T>

Similar to C++'s type_traits for handling optional values:


enum Option<T> {
    None,
    Some(T),
}

Functions return None for empty values or Some(value) for valid results.

Pattern Matching

match is an expression where all arms must return the same type. It complements enums perfectly:


fn analyze_message(msg: Message) {
    match msg {
        Message::Quit => println!("Exiting"),
        Message::Move { x, y } => println!("Moving to ({}, {})", x, y),
        Message::Write(text) => println!("Writing: {}", text),
        Message::ChangeColor(r, g, b) => println!("RGB: ({}, {}, {})", r, g, b),
    }
}

Syntactic sugar if let simplifies single-variant matches.


Day 3 (2021/05/30)

Package Management

Cargo manages Rust projects with these concepts:

  • Packages: Collections of source files with Cargo.toml
  • Crates: Module trees (library or binary)
  • Modules: Control scope and privacy
  • Paths: Naming items like structs, functions, modules
Collections
Vector
  • Provides vec! macro for initialization
  • Prevents iterator invalidation through borrow checking
  • Grows by factor of 2 like most implementations
String
  • Built-in str (usually as borrowed &str)
  • Owned String for mutable operations
  • UTF-8 encoded, Vec<u8> internally
  • No random access to avoid invalid UTF-8 slices
HashMap
  • Key-value store with ownership rules
  • Access requires careful reference handling
Exercise: Statistics

fn calculate_mean(values: &Vec<i32>) -> Option<f64> {
    if values.is_empty() {
        return None;
    }
    
    let sum: i32 = values.iter().sum();
    Some(sum as f64 / values.len() as f64)
}

fn find_median(values: &Vec<i32>) -> Option<i32> {
    if values.is_empty() {
        return None;
    }
    
    let mut sorted = values.clone();
    sorted.sort();
    Some(sorted[sorted.len() / 2])
}

fn determine_mode(values: &Vec<i32>) -> Option<i32> {
    if values.is_empty() {
        return None;
    }
    
    let mut frequencies = HashMap::new();
    for value in values.iter() {
        *frequencies.entry(value).or_insert(0) += 1;
    }
    
    frequencies.iter()
        .max_by_key(|&(_, count)| count)
        .map(|(&value, _)| *value)
}


Day 4 (2021/05/31)

Error Handling

panic! terminates execution. Use RUST_BACKTRACE=1 for stack traces.

Core error structure:


enum Result<T, E> {
    Ok(T),
    Err(E),
}

? operator propagates errors:


fn read_config() -> Result<Config, Error> {
    let content = std::fs::read_to_string("config.toml")?;
    Ok(parse_config(content)?)
}


Day 5 (2021/06/02)

Generics

Generic addition requires trait bounds:


use std::ops::Add;

fn add_values<T>(x: T, y: T) -> T 
where 
    T: Add<Output = T>,
{
    x + y
}

Traits

Traits define shared behavior, combining C++ type_traits and interface cocnepts:


pub trait Measurable {
    fn get_height(&self) -> usize;
    fn get_weight(&self) -> usize { 0 }
}

struct Person {
    name: String,
}

impl Measurable for Person {
    fn get_height(&self) -> usize {
        self.name.len()
    }
}

Lifetimes

Lifetime annotations prevent dangling references:


fn find_longer<'a>(first: &'a str, second: &'a str) -> &'a str {
    if first.len() > second.len() {
        first
    } else {
        second
    }
}


Day 6 (2021/06/06)

Closures

Closures capture environment with three traits:

  • FnOnce: consumes captured values
  • FnMut: mutably borrows captured values
  • Fn: immutably borrows captured values
Iterators

Iterator trait defines next() method:


pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}


Day 7 (2021/06/07)

Smart Pointers

Box<T> for heap allocation (like C++ unique_ptr)


struct CustomBox<T> {
    data: T,
}

impl<T> CustomBox<T> {
    fn create(value: T) -> CustomBox<T> {
        CustomBox { data: value }
    }
}

impl<T> std::ops::Deref for CustomBox<T> {
    type Target = T;
    
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

Rc<T> for reference counting (like shared_ptr)

RefCell<T> for interior mutability with runtime borrow checking

Concurrency

1:1 threading model with ownership transfer:


use std::thread;

fn main() {
    let data = vec![1, 2, 3];
    
    let handle = thread::spawn(move || {
        println!("Data: {:?}", data);
    });
    
    handle.join().unwrap();
}

Message passing via channels:


use std::sync::mpsc;

let (sender, receiver) = mpsc::channel();

thread::spawn(move || {
    sender.send("Hello").unwrap();
});

println!("Received: {:?}", receiver.recv());


Day 8 (2021/06/08)

Unsafe Code

Unsafe blocks for:

  • Raw pointer operations
  • Union access
  • Foreign function interface
  • Calling unsafe functions
Advanced Traits

Associated types as type aliases:


trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

Supertraits for trait dependencies:


trait Printable: std::fmt::Display {
    fn print_border(&self) {
        let output = self.to_string();
        println!("+{}+", "-".repeat(output.len()));
    }
}

Tags: rust ownership traits generics Concurrency

Posted on Sun, 24 May 2026 19:30:13 +0000 by stevietee