Language Semantics

Memory Safety

Rust ensures memory safety at compile-time through its ownership model. This guarantees that no use-after-free or dangling pointers exist in valid Rust programs.

fn main() {
 let s1 = String::from("hello");
 let s2 = s1; // Move
 // println!("{}", s1); // Compile error: variable moved
 println!("{}", s2); // Compilation OK
}
 

Ownership and Borrowing

Ownership determines how Rust manages memory. Each value has a variable that's called its owner. There can only be one owner at a time.

  • Ownership is transferred when values are assigned
  • Borrowing allows referencing values without taking ownership
  • References must be either & (immut) or &mut (mut)
fn main() {
 let s = String::from("hello");
 let p = &s; // immutable borrow
 let p_mut = &mut s; // mutable borrow
}
 

Lifetimes

Lifences ensure references are valid. The compiler uses this information to prevent dangling references and use-after-free errors.

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

The 'a lifetime parameter indicates that all references returned will be valid as long as the references passed in are valid

Borrow Checker

The borrow checker ensures that memory is used safely. It enforces the following rules:

  • Any number of immutable borrows
  • Or one mutable borrow
  • References are only valid for the scope they're in
fn main() {
 let mut s = String::from("hello");
 let r1 = &s; // no problem
 let r2 = &s; // no problem
 // let r3 = &mut s; // compiler error!
 println!("{} and {}", r1, r2);
}
 

Error Propagation

Rust provides explicit error handling with Result and Option types to ensure errors are properly handled:

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

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

Functions use Result to return either success or detailed error information:

fn divide(a: f64, b: f64) -> Result<f64, String> {
 if b == 0.0 {
 return Err("division by zero".to_string());
 }
 Ok(a / b)
}
 

Concurrency Semantics

Rust provides safe concurrency through a combination of threads and safe API:

  • Memory safety across threads
  • No need for locks in many cases
  • Cross-thread communication via channels
use std::thread;

fn main() {
 thread::spawn(|| {
 println!("Hello from a thread!");
 }).join().unwrap();
}