Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Essential Rust Concepts: Ownership, Structs, Enums, and Error Handling

Tech 2

Ownership in Rust

Ownership is a core concept in Rust that ensures memory safety without a garbage collector. It revolves around three key rules:

  • Each value has a single owner.
  • When the owner goes out of scope, the value is dropped.
  • References allow borrowing without transferring ownership.

Example illustrating ownership and borrowing:

fn modify_value(data: &mut i32) {
    let local_val = 5;
    let stack_ref = &local_val;
    
    let heap_val = Box::new(10);
    let heap_ref = &heap_val;
    let deref_heap = &*heap_val;
    
    *data += 5;
}

fn main() {
    let mut number = 0;
    modify_value(&mut number);
}

Structs for Organizing Data

Structs group related data in to custom types.

Defining and Instantiating Structs

Define a struct:

struct UserProfile {
    active: bool,
    username: String,
    email: String,
    login_count: u64,
}

Create an instance:

let profile1 = UserProfile {
    active: true,
    username: String::from("user123"),
    email: String::from("user@example.com"),
    login_count: 1,
};

Field init shorthand simplifies assignment when variable names match field names.

let username = String::from("user456");
let profile2 = UserProfile {
    username,
    active: false,
    email: String::from("another@example.com"),
    login_count: 0,
};

Struct update syntax creates a new instance based on an existing one.

let profile3 = UserProfile {
    email: String::from("new@example.com"),
    ..profile1
};

Tuple Structs and Unit Structs

Tuple structs have named types without named fields.

struct Color(u8, u8, u8);
let red = Color(255, 0, 0);

Unit structs have no fields.

struct Marker;
let marker = Marker;

Methods and Associated Functions

Define methods within impl blocks.

impl UserProfile {
    fn is_active(&self) -> bool {
        self.active
    }
    
    fn increment_login(&mut self) {
        self.login_count += 1;
    }
    
    fn create_default() -> Self {
        Self {
            active: true,
            username: String::from("default"),
            email: String::from("default@example.com"),
            login_count: 0,
        }
    }
}

Enums and Pattern Matching

Enums define types that can be one of several variants.

enum Transaction {
    Withdraw(f64),
    Deposit { amount: f64, account: String },
    Transfer,
}

The Option Enum

Rust uses Option<T> to handle absence of values safely.

let present_value: Option<i32> = Some(42);
let absent_value: Option<i32> = None;

Pattern Matchign with match

match allows exhaustive handling of enum variants.

fn process_transaction(tx: Transaction) -> String {
    match tx {
        Transaction::Withdraw(amt) => format!("Withdrawn: {}", amt),
        Transaction::Deposit { amount, account } => format!("Deposited {} to {}", amount, account),
        Transaction::Transfer => String::from("Transfer initiated"),
    }
}

Use if let for concise single-pattern matching.

if let Transaction::Withdraw(amt) = tx {
    println!("Withdrawal amount: {}", amt);
}

Error Handling

Rust distinguishes between recoverable errors with Result and unrecoverable errors with panic!.

Recoverable Errors with Result

Result<T, E> represents either success (Ok(T)) or failure (Err(E)).

use std::fs::File;
use std::io::{self, Read};

fn read_config() -> Result<String, io::Error> {
    let mut file = File::open("config.txt")?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    Ok(content)
}

The ? operator propagates errors concisely.

Unrecoverable Errors with panic!

Use panic! to unrecoverable errors.

fn validate_input(value: i32) {
    if value < 0 {
        panic!("Input must be non-negative");
    }
}

Collectionns

Vectors

Vectors store multiple values of the same type in a heap-allocated array.

let mut numbers: Vec<i32> = Vec::new();
numbers.push(10);
numbers.push(20);

let first = numbers[0];
let second = numbers.get(1);

for num in &numbers {
    println!("{}", num);
}

Strings

String is a growable, UTF-8 encoded text type.

let mut greeting = String::from("Hello");
greeting.push_str(", world!");

let combined = format!("{}{}", greeting, " Welcome");

Hash Maps

HashMap<K, V> stores key-value pairs with unique keys.

use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Red"), 20);

let blue_score = scores.get("Blue");

for (team, score) in &scores {
    println!("{}: {}", team, score);
}

Generics, Traits, and Lifetimse

Generics

Generics allow writing code that works with multiple types.

fn find_largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

Traits

Traits define shared behavior across types.

trait Summarizable {
    fn summary(&self) -> String;
}

struct Article {
    title: String,
    content: String,
}

impl Summarizable for Article {
    fn summary(&self) -> String {
        format!("{}: {}", self.title, &self.content[..50])
    }
}

Lifetimes

Lifetimes ensure references remain valid.

fn longest_string<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

Testing

Write tests using the #[test] attribute.

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn addition_works() {
        assert_eq!(2 + 2, 4);
    }
    
    #[test]
    #[should_panic]
    fn invalid_input_panics() {
        validate_input(-5);
    }
}

Run tests with cargo test.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.