- Introduction to Rust
- Getting Started
- Core Concepts
- Ownership, Borrowing, and Lifetimes
- Data Types & Structures
- Control Flow
- Functions, Closures, and Traits
- Error Handling
- Modules, Crates, and Packages
- Concurrency & Asynchronous Programming
- Macros & Metaprogramming
- Interfacing with C & FFI
- Rust for Solana Development
- Advanced Topics
- Useful Resources
- Rust is a systems programming language focused on safety, speed, and concurrency.
- Memory safety without garbage collection.
- Used in blockchain (e.g., Solana), web servers, embedded, and more.
- Install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - Project structure:
Cargo.toml(manifest),src/main.rs(entry point) - Build & run:
cargo build,cargo run - Hello World Example:
fn main() { println!("Hello, world!"); }
- Variables: Immutable by default, use
mutfor mutability.let x = 5; // immutable let mut y = 10; // mutable y += 2; println!("y: {}", y);
- Constants: Must have type annotation, set at compile time.
const PI: f64 = 3.1415; println!("PI: {}", PI);
- Shadowing: Redeclare variable with
let.let z = 1; let z = z + 2; // shadows previous z println!("z: {}", z);
- Ownership: Each value has a single owner; value dropped when owner goes out of scope.
let s = String::from("hello"); let t = s; // s is moved to t, s is no longer valid // println!("{}", s); // error! println!("{}", t);
- Borrowing: References (
&Tfor immutable,&mut Tfor mutable).fn print_length(s: &String) { println!("Length: {}", s.len()); } let s = String::from("borrowed"); print_length(&s);
- Mutable Borrowing:
fn add_exclamation(s: &mut String) { s.push('!'); } let mut s = String::from("hi"); add_exclamation(&mut s); println!("{}", s);
- Lifetimes: Ensure references are valid.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } let a = "abc"; let b = "longer string"; println!("{}", longest(a, b));
- Scalar Types:
i32,u32,f64,bool,charlet a: i32 = -42; let b: f64 = 3.14; let c: bool = true; let d: char = 'R';
- Compound Types: Tuples
(i32, f64), Arrays[i32; 3]let tup: (i32, &str) = (10, "tuple"); let arr: [u8; 3] = [1, 2, 3]; println!("{} {}", tup.0, tup.1); println!("First: {}", arr[0]);
- Structs: Custom data types.
struct User { name: String, age: u8, } let user = User { name: String::from("Alice"), age: 30 }; println!("{} is {}", user.name, user.age);
- Tuple Structs:
struct Color(u8, u8, u8); let black = Color(0, 0, 0); println!("Black: {}, {}, {}", black.0, black.1, black.2);
- Enums: Tagged unions.
enum Direction { North, East, South, West } let dir = Direction::East; match dir { Direction::North => println!("Up"), Direction::East => println!("Right"), Direction::South => println!("Down"), Direction::West => println!("Left"), }
- Pattern Matching:
match,if letlet some_number = Some(7); match some_number { Some(n) if n > 5 => println!("Big number: {}", n), Some(n) => println!("Small number: {}", n), None => println!("No number"), }
- if/else: Standard conditional logic.
let temp = 30; if temp > 25 { println!("Hot!"); } else { println!("Cool!"); }
- loop, while, for: Iteration constructs.
let mut count = 0; loop { if count == 3 { break; } println!("loop: {}", count); count += 1; } let mut n = 0; while n < 3 { println!("while: {}", n); n += 1; } for i in 0..3 { println!("for: {}", i); }
- break, continue, return: Control statements.
for i in 0..5 { if i == 2 { continue; } if i == 4 { break; } println!("i: {}", i); } fn early_return(x: i32) -> i32 { if x > 0 { return x; } 0 } println!("{}", early_return(5));
- Functions:
fn square(x: i32) -> i32 { x * x } println!("{}", square(4));
- Closures: Anonymous functions, can capture environment.
let numbers = vec![1, 2, 3]; let doubled: Vec<_> = numbers.iter().map(|x| x * 2).collect(); println!("{:?}", doubled);
- Traits: Define shared behavior (interfaces).
trait Greet { fn greet(&self); } struct Person; impl Greet for Person { fn greet(&self) { println!("Hello!"); } } let p = Person; p.greet();
- Impl: Implement methods/traits for types.
struct Counter { value: i32 } impl Counter { fn increment(&mut self) { self.value += 1; } } let mut c = Counter { value: 0 }; c.increment(); println!("{}", c.value);
- Panic: Unrecoverable errors (
panic!macro).// panic!("Something went wrong!"); - Result & Option: Recoverable errors and optional values.
fn safe_divide(x: f64, y: f64) -> Result<f64, String> { if y == 0.0 { Err(String::from("Cannot divide by zero")) } else { Ok(x / y) } } match safe_divide(10.0, 2.0) { Ok(result) => println!("Result: {}", result), Err(e) => println!("Error: {}", e), }
- ? Operator: Propagate errors easily.
use std::fs::File; fn open_file() -> std::io::Result<File> { let f = File::open("foo.txt")?; Ok(f) }
- Modules: Organize code with
mod,pub mod.mod math { pub fn add(a: i32, b: i32) -> i32 { a + b } } println!("{}", math::add(2, 3));
- Crates: Compilation units (binary or library).
- Example:
randcrate for random numbers.
use rand::Rng; let mut rng = rand::thread_rng(); let n: u8 = rng.gen_range(0..10); println!("Random: {}", n);
- Example:
- Packages: One or more crates, managed by Cargo.
- Example: A package with both a binary and a library crate.
- Use: Import items with
use.use std::collections::HashMap; let mut map = HashMap::new(); map.insert("key", 42); println!("{:?}", map);
- Threads:
std::thread::spawnuse std::thread; let handle = thread::spawn(|| { println!("Hello from a thread!"); }); handle.join().unwrap();
- Channels: Message passing (
std::sync::mpsc)use std::sync::mpsc; let (tx, rx) = mpsc::channel(); std::thread::spawn(move || { tx.send(42).unwrap(); }); println!("Received: {}", rx.recv().unwrap());
- Mutex, Arc: Shared state concurrency.
use std::sync::{Arc, Mutex}; let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..5 { let counter = Arc::clone(&counter); let handle = std::thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap());
- Async/Await: Asynchronous programming (
async fn,.await)async fn say_hello() { println!("Hello async!"); } // In main: futures::executor::block_on(say_hello());
- Tokio, async-std: Popular async runtimes.
- Example: Tokio timer
// tokio::time::sleep(std::time::Duration::from_secs(1)).await;
- Macros: Code generation (
macro_rules!, procedural macros)macro_rules! repeat { ($val:expr, $times:expr) => { for _ in 0..$times { println!("{}", $val); } }; } repeat!("Hi", 3);
- Derive Macros:
#[derive(Debug, Clone)]#[derive(Debug, Clone)] struct Point { x: i32, y: i32 } let p = Point { x: 1, y: 2 }; println!("{:?}", p.clone());
- FFI: Call C code with
extern "C"andunsafeblocks.extern "C" { fn abs(input: i32) -> i32; } unsafe { println!("Abs: {}", abs(-3)); }
- Bindgen: Generate Rust bindings for C libraries.
- Example: Use
bindgenCLI to generate Rust FFI bindings from C headers.
- Example: Use
- Solana Programs: On-chain programs (smart contracts) written in Rust.
// Entry point for a Solana program use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey}; entrypoint!(process_instruction); fn process_instruction( _program_id: &Pubkey, _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { Ok(()) }
- Borsh/Serde: Serialization frameworks for account data.
use borsh::{BorshDeserialize, BorshSerialize}; #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct MyAccount { pub data: u64 }
- Anchor Framework: Abstraction for Solana programs, uses Rust macros and attributes.
use anchor_lang::prelude::*; #[account] pub struct MyData { pub value: u64 }
- CPI (Cross-Program Invocation): Call other Solana programs from your program.
// Example: invoke CPI using anchor_lang::solana_program::program::invoke - Accounts: Use
#[account]to define account structures.#[account] pub struct UserAccount { pub authority: Pubkey, pub balance: u64 }
- Security: Emphasize safe deserialization, access control, and rent-exemption.
- Example: Check signer and rent exemption in instruction handler.
- Testing: Use
solana-program-test,Anchor's test framework.// Example: #[tokio::test] async fn test_my_program() { ... }
- Unsafe Rust: Manual memory management, raw pointers.
let x: i32 = 42; let r: *const i32 = &x; unsafe { println!("Value: {}", *r); }
- Zero-cost Abstractions: Compile-time optimizations.
- Example: Iterators are optimized away at compile time.
let v = vec![1, 2, 3]; let sum: i32 = v.iter().sum(); println!("Sum: {}", sum);
- Lifetime Elision: Compiler infers lifetimes in simple cases.
fn first(s: &str) -> &str { &s[0..1] } println!("{}", first("hello"));
- Smart Pointers:
Box,Rc,Arc,RefCell.use std::rc::Rc; let a = Rc::new(5); let b = Rc::clone(&a); println!("{} {}", a, b);
- Trait Objects: Dynamic dispatch with
dyn Trait.trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } let animal: Box<dyn Animal> = Box::new(Dog); animal.speak();
- Generics & Associated Types: Powerful type abstractions.
fn largest<T: PartialOrd>(list: &[T]) -> &T { let mut largest = &list[0]; for item in list { if item > largest { largest = item; } } largest } println!("{}", largest(&[1, 2, 3]));
- Custom Derive & Procedural Macros: Metaprogramming for frameworks like Anchor.
- Example:
#[derive(Accounts)]in Anchor.
- Example:
This guide is a high-level overview. For in-depth learning, refer to the official documentation and community resources.