
Rust Cheatsheet
25 of 25 code examples
Hello World
Basic Rust program
Basicsfn main() {
println!("Hello, World!");
}
Variables
Variable declarations and mutability
Basics// Immutable by default
let x = 5;
let name = "Alice";
// Mutable variable
let mut count = 0;
count += 1;
// Constants
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159;
// Shadowing
let spaces = " ";
let spaces = spaces.len(); // Different type
// Type annotations
let age: u32 = 30;
let price: f64 = 19.99;
let is_active: bool = true;
Data Types
Basic data types
Basics// Scalar types
let integer: i32 = 42;
let unsigned: u64 = 100;
let float: f64 = 3.14;
let boolean: bool = true;
let character: char = '🦀';
// Compound types
let tuple: (i32, f64, char) = (500, 6.4, 'z');
let (x, y, z) = tuple; // Destructuring
// Arrays
let array: [i32; 5] = [1, 2, 3, 4, 5];
let first = array[0];
let sized_array = [0; 10]; // 10 zeros
// Slices
let slice = &array[1..4]; // [2, 3, 4]
Functions
Function definitions
Functions// Basic function
fn add(x: i32, y: i32) -> i32 {
x + y
}
// Expression vs statement
fn five() -> i32 {
5 // No semicolon = expression (return value)
}
// Multiple parameters
fn print_coordinates(x: i32, y: i32) {
println!("Coordinates: ({}, {})", x, y);
}
// Early return
fn safe_divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
return None;
}
Some(a / b)
}
// Function pointers
fn apply<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32
{
f(x)
}
Control Flow
If, loops, and match
Basics// If expressions
let number = 6;
if number % 2 == 0 {
println!("even");
} else {
println!("odd");
}
// If as expression
let result = if number > 5 { "big" } else { "small" };
// Loop
let mut counter = 0;
loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
}
// While loop
while counter > 0 {
println!("{}!", counter);
counter -= 1;
}
// For loop
for element in [10, 20, 30].iter() {
println!("{}", element);
}
// Match expression
match number {
1 => println!("One"),
2 | 3 | 5 | 7 => println!("Prime"),
_ => println!("Other"),
}
Ownership
Rust's ownership system
Memory// Ownership transfer
let s1 = String::from("hello");
let s2 = s1; // s1 is no longer valid here
// println!("{}", s1); // This would cause compile error
// Clone for deep copy
let s3 = s2.clone();
println!("s2 = {}, s3 = {}", s2, s3);
// Function and ownership
fn take_ownership(s: String) {
println!("{}", s);
} // s goes out of scope and memory is freed
let s4 = String::from("hello");
take_ownership(s4);
// s4 is no longer valid here
// Return ownership
fn give_ownership() -> String {
let s = String::from("hello");
s // Ownership is returned
}
References & Borrowing
Borrowing and references
Memory// Immutable reference
fn calculate_length(s: &String) -> usize {
s.len()
} // s goes out of scope but doesn't drop what it refers to
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("Length of '{}' is {}", s1, len);
// Mutable reference
fn change(s: &mut String) {
s.push_str(", world");
}
let mut s2 = String::from("hello");
change(&mut s2);
println!("{}", s2);
// Only one mutable reference
let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = &mut s; // This would cause compile error
// Multiple immutable references allowed
let r3 = &s;
let r4 = &s;
println!("{} and {}", r3, r4);
Structs
Custom data types
Data Structures// Struct definition
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// Creating instances
let user1 = User {
email: String::from("user@example.com"),
username: String::from("user123"),
active: true,
sign_in_count: 1,
};
// Mutable struct
let mut user2 = User {
email: String::from("another@example.com"),
username: String::from("user456"),
active: true,
sign_in_count: 1,
};
user2.sign_in_count = 2;
// Field init shorthand
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
// Tuple structs
struct Color(i32, i32, i32);
let black = Color(0, 0, 0);
Enums & Pattern Matching
Enums and match expressions
Data Structures// Basic enum
enum IpAddrKind {
V4,
V6,
}
// Enum with data
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
// Option enum
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
// Match expression
match divide(10.0, 2.0) {
Some(result) => println!("Result: {}", result),
None => println!("Cannot divide by zero!"),
}
// if let syntax
let some_value = Some(3);
if let Some(3) = some_value {
println!("three");
}
Methods & Traits
Implementing methods and traits
OOP// Method implementation
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// Associated function (like static method)
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
// Method with &self
fn area(&self) -> u32 {
self.width * self.height
}
// Method with &mut self
fn double(&mut self) {
self.width *= 2;
self.height *= 2;
}
}
// Using methods
let mut rect = Rectangle { width: 30, height: 50 };
println!("Area: {}", rect.area());
rect.double();
// Trait definition
trait Summary {
fn summarize(&self) -> String;
// Default implementation
fn preview(&self) -> String {
String::from("(Read more...)")
}
}
// Trait implementation
struct NewsArticle {
headline: String,
location: String,
author: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
Generics
Generic types and functions
Advanced// Generic function
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
// Generic struct
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// Multiple generic types
struct Pair<T, U> {
first: T,
second: U,
}
// Using generics
let integer_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
// Generic with trait bounds
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// where clause for complex bounds
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
// function body
42
}
Error Handling
Result and Option types
Error Handling// Result enum
enum Result<T, E> {
Ok(T),
Err(E),
}
// Using Result
fn read_file(path: &str) -> Result<String, std::io::Error> {
std::fs::read_to_string(path)
}
// Handling errors with match
match read_file("hello.txt") {
Ok(content) => println!("File content: {}", content),
Err(error) => println!("Error reading file: {}", error),
}
// Using ? operator for propagation
fn read_username_from_file() -> Result<String, std::io::Error> {
let mut s = String::new();
std::fs::File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
// Option type
fn find_user(id: u32) -> Option<String> {
if id == 1 {
Some(String::from("Alice"))
} else {
None
}
}
// Combinators
let username = find_user(1)
.map(|name| name.to_uppercase())
.unwrap_or(String::from("Unknown"));
// unwrap and expect
let file = std::fs::File::open("config.txt").expect("Failed to open config file");
Collections - Vector
Dynamic arrays
Data Structures// Creating vectors
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
// Vector macro
let mut v2 = vec![1, 2, 3];
v2.push(4);
// Accessing elements
let third = &v2[2]; // Panics if out of bounds
println!("The third element is {}", third);
let third = v2.get(2); // Returns Option
match third {
Some(value) => println!("The third element is {}", value),
None => println!("There is no third element"),
}
// Iterating
for i in &v2 {
println!("{}", i);
}
// Iterating mutable
for i in &mut v2 {
*i += 50;
}
// Using enums for multiple types
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
Collections - String
String manipulation
Data Structures// Creating strings
let mut s = String::new();
let data = "initial contents";
let s = data.to_string();
let s = String::from("initial contents");
// Updating strings
let mut s = String::from("foo");
s.push_str("bar");
s.push('!');
// Concatenation
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // Note: s1 has been moved here
// Format macro
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
// Indexing (not directly supported)
let hello = "Здравствуйте";
// let h = hello[0]; // This won't compile!
// Slicing strings
let s = &hello[0..4]; // First 2 characters (4 bytes)
println!("{}", s); // Prints "Зд"
// Iterating over strings
for c in "नमस्ते".chars() {
println!("{}", c);
}
for b in "नमस्ते".bytes() {
println!("{}", b);
}
Collections - HashMap
Key-value storage
Data Structuresuse std::collections::HashMap;
// Creating HashMap
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// Accessing values
let team_name = String::from("Blue");
let score = scores.get(&team_name);
match score {
Some(value) => println!("Score: {}", value),
None => println!("Team not found"),
}
// Iterating
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// Updating values
// Overwriting
scores.insert(String::from("Blue"), 25);
// Only insert if key has no value
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Red")).or_insert(30);
// Update based on old value
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}", map);
Lifetimes
Explicit lifetime annotations
Advanced// Lifetime annotations
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
// Using the function
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
// Lifetime in structs
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
// Using the struct
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
// Static lifetime
let s: &'static str = "I have a static lifetime.";
// Lifetime elision rules
fn first_word(s: &str) -> &str { // Compiler infers lifetimes
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
Closures
Anonymous functions
Functions// Basic closure
let add_one = |x: i32| -> i32 { x + 1 };
let result = add_one(5);
// Type inference
let add_one_v2 = |x| x + 1;
let result2 = add_one_v2(5);
// Capturing environment
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));
// Move closure (takes ownership)
let x = vec![1, 2, 3];
let equal_to_x = move |z| z == x;
// println!("{:?}", x); // Error: x was moved
// Closure traits
// Fn: borrows values immutably
// FnMut: borrows values mutably
// FnOnce: takes ownership
// Using closures with iterators
let v1 = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(v2, vec![2, 3, 4]);
// Storing closures
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
}
Iterators
Iterator pattern and methods
Data Structures// Creating iterators
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
// Using iterators
for val in v1_iter {
println!("Got: {}", val);
}
// Iterator adapters (lazy)
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
// Consumer methods
let total: i32 = v1.iter().sum();
let any_gt_2 = v1.iter().any(|&x| x > 2);
let all_gt_0 = v1.iter().all(|&x| x > 0);
// Chaining methods
let names = vec!["alice", "bob", "carol"];
let uppercase_names: Vec<String> = names
.iter()
.map(|s| s.to_uppercase())
.filter(|s| s.len() > 3)
.collect();
// Implementing Iterator trait
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 5 {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
// Using custom iterator
let mut counter = Counter::new();
while let Some(count) = counter.next() {
println!("{}", count);
}
Smart Pointers
Box, Rc, Arc, RefCell
Memory// Box - heap allocation
let b = Box::new(5);
println!("b = {}", b);
// Recursive types with Box
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))));
// Rc - reference counting
use std::rc::Rc;
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));
// Arc - atomic reference counting (thread-safe)
use std::sync::Arc;
use std::thread;
let counter = Arc::new(0);
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
// Use counter
});
handles.push(handle);
}
// RefCell - interior mutability
use std::cell::RefCell;
let x = RefCell::new(42);
{
let mut y = x.borrow_mut();
*y += 1;
}
println!("{:?}", x.borrow());
Concurrency - Threads
Spawning threads
Concurrencyuse std::thread;
use std::time::Duration;
// Spawning a thread
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
// Main thread work
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
// Wait for thread to finish
handle.join().unwrap();
// Moving ownership into thread
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
Concurrency - Channels
Message passing between threads
Concurrencyuse std::sync::mpsc;
use std::thread;
// Multiple Producer, Single Consumer
let (tx, rx) = mpsc::channel();
// Spawn thread and send message
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
});
// Receive message
let received = rx.recv().unwrap();
println!("Got: {}", received);
// Multiple messages
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
for val in vals {
tx1.send(val).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
thread::spawn(move || {
let vals = vec![
String::from("more"),
String::from("messages"),
String::from("for"),
String::from("you"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
for received in rx {
println!("Got: {}", received);
}
Concurrency - Mutex
Shared state concurrency
Concurrencyuse std::sync::{Mutex, Arc};
use std::thread;
// Single-threaded mutex
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!("m = {:?}", m);
// Multi-threaded mutex with Arc
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = 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());
// Deadlock prevention
let mutex1 = Arc::new(Mutex::new(0));
let mutex2 = Arc::new(Mutex::new(0));
// Always acquire locks in the same order
let handle1 = {
let m1 = Arc::clone(&mutex1);
let m2 = Arc::clone(&mutex2);
thread::spawn(move || {
let _lock1 = m1.lock().unwrap();
let _lock2 = m2.lock().unwrap();
// critical section
})
};
Modules & Crates
Code organization
Packages// Cargo.toml
// [package]
// name = "my_crate"
// version = "0.1.0"
// edition = "2021"
//
// [dependencies]
// serde = "1.0"
// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
// src/main.rs
use my_crate::eat_at_restaurant;
fn main() {
eat_at_restaurant();
}
// File hierarchy
// src/
// lib.rs
// main.rs
// front_of_house.rs
// front_of_house/
// hosting.rs
// serving.rs
Real-World: Web Server
Async web server with Tokio
Real Worlduse std::convert::Infallible;
use std::net::SocketAddr;
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
async fn hello_world(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("Hello, World!")))
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let make_svc = make_service_fn(|_conn| {
async {
Ok::<_, Infallible>(service_fn(hello_world))
}
});
let server = Server::bind(&addr).serve(make_svc);
println!("Server running on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}
// With routing (using warp)
// use warp::Filter;
//
// #[tokio::main]
// async fn main() {
// let hello = warp::path!("hello" / String)
// .map(|name| format!("Hello, {}!", name));
//
// warp::serve(hello)
// .run(([127, 0, 0, 1], 3030))
// .await;
// }
Real-World: CLI Tool
Command-line application
Real Worlduse std::env;
use std::fs;
use std::process;
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
});
if let Err(e) = run(config) {
eprintln!("Application error: {}", e);
process::exit(1);
}
}
struct Config {
query: String,
filename: String,
case_sensitive: bool,
}
impl Config {
fn new(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}
let query = args[1].clone();
let filename = args[2].clone();
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
Ok(Config {
query,
filename,
case_sensitive,
})
}
}
fn run(config: Config) -> Result<(), Box<dyn std::error::Error>> {
let contents = fs::read_to_string(config.filename)?;
let results = if config.case_sensitive {
search(&config.query, &contents)
} else {
search_case_insensitive(&config.query, &contents)
};
for line in results {
println!("{}", line);
}
Ok(())
}
fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents
.lines()
.filter(|line| line.contains(query))
.collect()
}
fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
contents
.lines()
.filter(|line| line.to_lowercase().contains(&query))
.collect()
}