Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Rust Programming Fundamentals: Syntax, Data Structures, and Memory Management

Tech 1
fn greet_world() {
    println!("Hello, world!");
    let german_greeting = "Grüß Gott!";
    let japanese_greeting = "ハロー・ワールド";
    let greetings = [german_greeting, japanese_greeting];
    for greeting in greetings.iter() {
        println!("{}", greeting);
    }
}

fn main() {
    greet_world();
}

println! is a macro that performs extansive type checking at compile time to support various data types.

fn main() {
    let penguin_data = "\
common name,length (cm)
Little penguin,33
Yellow-eyed penguin,65
Fiordland penguin,60
Invalid,data
";

    for (index, record) in penguin_data.lines().enumerate() {
        if index == 0 || record.trim().is_empty() {
            continue;
        }

        let fields: Vec<_> = record
            .split(',')
            .map(|field| field.trim())
            .collect();

        if cfg!(debug_assertions) {
            eprintln!("debug: {:?} -> {:?}", record, fields);
        }

        let name = fields[0];
        if let Ok(length) = fields[1].parse::<f32>() {
            println!("{}, {}cm", name, length);
        }
    }
}

Memory Safety

Dangling Poniters

#[derive(Debug)]
enum Cereal {
    Barley,
    Millet,
    Rice,
    Rye,
    Spelt,
    Wheat,
}

fn main() {
    let mut grains: Vec<Cereal> = vec![];
    grains.push(Cereal::Rye);
    drop(grains);
    println!("{:?}", grains);
}

Data Races

use std::thread;

fn main() {
    let mut data = 100;
    
    thread::spawn(|| { data = 500; });
    thread::spawn(|| { data = 1000; });
    println!("{}", data);
}

Iterator Invalidation

fn main() {
    let mut letters = vec!["a", "b", "c"];
    
    for letter in &letters {
        println!("{}", letter);
        letters.push(letter.clone());
    }
}

Basic Syntax

fn main() {
    let a = 10;
    let b: i32 = 20;
    let c = 30i32;
    let d = 30_i32;
    let result = add(add(a, b), add(c, d));
    println!("(a + b) + (c + d) = {}", result);
}

fn add(x: i32, y: i32) -> i32 {
    x + y
}

Loops and Control Flow

fn main() {
    for _ in 0..10 {
        print!("a")
    }
}
fn main() {
    for n in 0..10 {
        if n % 2 == 0 {
            continue;
        }
        print!("{}", n)
    }
}
use std::time::{Duration, Instant};

fn main() {
    let mut count = 0;
    let time_limit = Duration::new(1, 0);
    let start = Instant::now();

    while (Instant::now() - start) < time_limit {
        count += 1;
    }
    println!("{}", count);
}

References and Borrowing

fn main() {
    let a = 42;
    let r = &a;
    let sum = a + *r;
    println!("a + a = {}", sum);
}

Lifetimes

fn add_with_lifetimes<'a, 'b>(x: &'a i32, y: &'b i32) -> i32 {
    *x + *y
}

fn main() {
    let a = 10;
    let b = 20;
    let result = add_with_lifetimes(&a, &b);
    println!("{}", result);
}

Generics

use std::ops::Add;
use std::time::Duration;

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

fn main() {
    let floats = add(1.2, 3.4);
    let ints = add(10, 20);
    let durations = add(Duration::new(5, 0), Duration::new(10, 0));
    println!("{}", floats);
    println!("{}", ints);
    println!("{:?}", durations);
}

Arrays and Vectors

fn main() {
    let arr1 = [1, 2, 3];
    let arr2: [u8; 3] = [1, 2, 3];
    let blank1 = [0; 3];
    let blank2: [u8; 3] = [0; 3];

    let arrays = [arr1, arr2, blank1, blank2];

    for arr in &arrays {
        print!("{:?}: ", arr);
        for n in arr.iter() {
            print!("\t{} + 10 = {}", n, n + 10);
        }
        println!();
    }
}

Structs and Methods

#[derive(Debug)]
struct File {
    name: String,
    data: Vec<u8>,
}

impl File {
    fn new(name: &str) -> File {
        File {
            name: String::from(name),
            data: Vec::new(),
        }
    }
}

fn main() {
    let file = File::new("example.txt");
    println!("{:?}", file);
}

Error Handling with Result

use rand::prelude::*;

fn one_in(denominator: u32) -> bool {
    thread_rng().gen_ratio(1, denominator)
}

#[derive(Debug)]
struct File {
    name: String,
    data: Vec<u8>,
}

impl File {
    fn new(name: &str) -> File {
        File {
            name: String::from(name),
            data: Vec::new(),
        }
    }
}

fn open(file: File) -> Result<File, String> {
    if one_in(10_000) {
        return Err(String::from("Permission denied"));
    }
    Ok(file)
}

fn main() {
    let file = File::new("test.txt");
    match open(file) {
        Ok(f) => println!("File opened: {:?}", f),
        Err(e) => println!("Error: {}", e),
    }
}

Anums

#[derive(Debug)]
enum Event {
    Update,
    Delete,
    Unknown,
}

type Message = String;

fn parse_log(line: &str) -> (Event, Message) {
    let parts: Vec<_> = line.splitn(2, ' ').collect();
    if parts.len() == 1 {
        return (Event::Unknown, String::from(line));
    }

    let event = parts[0];
    let message = String::from(parts[1]);

    match event {
        "UPDATE" | "update" => (Event::Update, message),
        "DELETE" | "delete" => (Event::Delete, message),
        _ => (Event::Unknown, String::from(line)),
    }
}

Memory Management and Pointers

fn main() {
    let a: i64 = 42;
    let a_ptr = &a as *const i64;
    println!("a: {} ({:p})", a, a_ptr);
}

Heap Allocation

use std::mem::drop;

fn main() {
    let a = Box::new(1);
    let b = Box::new(1);
    let c = Box::new(1);

    let result1 = *a + *b + *c;
    drop(a);
    let d = Box::new(1);
    let result2 = *b + *c + *d;

    println!("{} {}", result1, result2);
}

CPU Emulation Example

struct CPU {
    registers: [u8; 16],
    position_in_memory: usize,
    memory: [u8; 4096],
    stack: [u16; 16],
    stack_pointer: usize,
}

impl CPU {
    fn read_opcode(&self) -> u16 {
        let p = self.position_in_memory;
        let byte1 = self.memory[p] as u16;
        let byte2 = self.memory[p + 1] as u16;
        byte1 << 8 | byte2
    }

    fn run(&mut self) {
        loop {
            let opcode = self.read_opcode();
            self.position_in_memory += 2;

            let c = ((opcode & 0xF000) >> 12) as u8;
            let x = ((opcode & 0x0F00) >> 8) as u8;
            let y = ((opcode & 0x00F0) >> 4) as u8;
            let d = ((opcode & 0x000F) >> 0) as u8;

            match (c, x, y, d) {
                (0, 0, 0, 0) => return,
                (0x8, _, _, 0x4) => self.add_xy(x, y),
                _ => todo!("opcode {:04x}", opcode),
            }
        }
    }

    fn add_xy(&mut self, x: u8, y: u8) {
        let (result, overflow) = self.registers[x as usize]
            .overflowing_add(self.registers[y as usize]);
        self.registers[x as usize] = result;
        self.registers[0xF] = if overflow { 1 } else { 0 };
    }
}

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.