Rust Programming Fundamentals: Syntax, Data Structures, and Memory Management
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 };
}
}