Rust Snippets - A collection of Rust programs and some very brief docs.
My sincere thanks to Google for making information easily available to the masses and for open-sourcing many software.
And, of course, to the Rust Community
With the advent of AI-assisted coding, now it is all about creativity and imagination in gathering and presenting code and paving the way for hierarchical and/or chained AI systems.
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// writes a string to stdout
println!("hello, rust universe!");
// writes a string to stderr
eprintln!("damn, encountered an error!");
// writes a string to a file
fs::write("out.txt", "just a line to go into out.txt\n")?;
// Reads the entire file into a String; propagates errors with '?'
let contents = fs::read_to_string("out.txt")?;
println!("{}", contents);
// will throw error if the file does not exist
let contents = fs::read_to_string("foo.txt")?;
println!("{}", contents);
// indicates success
Ok(())
}
Save above into a file called main.rs
$ touch foo.txt; rustc main.rs && ./main # no error message
$ rm -f foo.txt; rustc main.rs && ./main # shows error message
use std::env;
fn main() {
let key = "DATABASE_URL";
// 1. Basic reading (will panic if missing)
let db_url = env::var(key).unwrap();
// 2. Safe pattern matching for missing variables
match env::var(key) {
Ok(val) => println!("{key} is set to: {val}"),
Err(e) => println!("Could not read {key}: {e}"),
}
// 3. Providing a default value if missing
let port = env::var("PORT").unwrap_or_else(|_| "8080".to_string());
}
use std::env;
fn main() {
// Collect all arguments into a Vector of Strings
let args: Vec<String> = env::args().collect();
// The first argument (index 0) is always the path to the executable
println!("Executable path: {}", args[0]);
// Check if the user passed actual arguments
if args.len() > 1 {
println!("First user argument: {}", args[1]);
println!("All user arguments: {:?}", &args[1..]);
} else {
println!("No user arguments provided.");
}
}
pub struct User {
name: String,
age: u32,
}
impl User {
// This acts as your trigger
pub fn new(name: String, age: u32) -> Self {
let user = Self { name, age };
// Trigger your event here
// `Note`: whatever data comes in here can be sent asynchronously to any resource, like a (distributed) database/datastore/storage-system or a (distributed) messaging system.
println!("Trigger: A new user named {} was created!", user.name);
user
}
}
fn main() {
// A simple closure that adds one to a number
let add_one = |x: i32| x + 1;
let result = add_one(5); // Returns 6
println!("{}", result);
}
fn main() {
let name = "Rust";
// Captures `name` by immutable reference (&T)
let print_name = || println!("Hello, {}!", name);
print_name();
print_name(); // Can call again because `name` is still valid
}
fn main() {
let mut count = 0;
// Captures `count` by mutable reference (&mut T)
let mut increment = || {
count += 1;
println!("Count: {}", count);
};
increment(); // Count: 1
increment(); // Count: 2
}
fn main() {
let data = vec![1, 2, 3];
// Uses the `move` keyword to take ownership of `data`
let consume_data = move || {
// note: whatever data comes in here can be sent asynchronously to any resource, like a database
let _len = data.len();
println!("Consumed data!");
};
consume_data();
// consume_data(); // Error! `consume_data` cannot be called twice.
// println!("{:?}", data); // Error! `data` was moved into the closure.
}
use std::fs::File;
use std::io::{self, ErrorKind};
fn get_or_create_file(path: &str) -> io::Result<File> {
match File::open(path) {
Ok(file) => Ok(file),
Err(error) => match error.kind() {
ErrorKind::NotFound => File::create(path),
_ => Err(error), // Return any other error (e.g. permissions)
},
}
}
fn main() {
let _file = get_or_create_file("output.txt").unwrap();
}
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let concatenated_string = format!("{} {} {}", "so", "be", "it");
println!("concatenated_string: {concatenated_string}");
Ok(())
}
use std::thread;
fn main() {
// Spawn a new thread
let handle = thread::spawn(|| {
println!("Hello from the spawned thread!");
});
// Wait for the thread to finish executing
handle.join().unwrap();
}
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct MyError;
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "My custom error")
}
}
impl Error for MyError {}
fn example_function() -> Result<(), Box<dyn Error>> {
Err(Box::new(MyError))
}
fn main() {
match example_function() {
Ok(r) => println!("{:?}", r),
Err(e) => println!("{e}"),
}
}
// This function accepts both &str and String seamlessly
fn route_any_text<T: AsRef<str>>(param: T) {
let text: &str = param.as_ref();
println!("Processing text: {}", text);
}
fn main() {
let my_str: &str = "hello";
let my_string: String = String::from("world");
// Both work perfectly
route_any_text(my_str);
route_any_text(my_string);
}
// 1. Define a routing trait
trait TextRouter {
fn route(self);
}
// 2. Implement behavior for &str
impl TextRouter for &str {
fn route(self) {
println!("Routed to the BORROWED (&str) handler: {}", self);
}
}
// 3. Implement behavior for String
impl TextRouter for String {
fn route(self) {
println!("Routed to the OWNED (String) handler: {}", self);
}
}
// 4. Create the entry point function
fn process_route<T: TextRouter>(param: T) {
param.route();
}
fn main() {
let borrowed: &str = "static_path";
let owned: String = String::from("dynamic_path");
// Executes &str logic
process_route(borrowed);
// Executes String logic
process_route(owned);
}
use std::any::type_name;
fn type_of<T>(_: T) -> &'static str {
type_name::<T>()
}
fn main() {
let a = 21; // Integer
println!("{}", type_of(a)); // i32
let b = 2.5; // Float
println!("{}", type_of(b)); // f64
let c = "hello";
println!("{}", type_of(c)); // &str
let d: String = "world".into();
println!("{}", type_of(d)); // alloc::string::String
struct S;
let the_struct = S;
println!("{}", type_of(the_struct)); // ...::main::S
}
// Declare the external C library function
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
// Calling the FFI function requires an unsafe block
let result = unsafe { abs(-42) };
println!("Absolute value from C: {}", result);
}
use std::process::Command;
fn main() {
// Spawn the binary
let mut child = Command::new("mkdir")
.arg("-p")
.arg("/tmp/a")
.spawn()
.expect("failed to execute process");
// Do other work while the child process runs...
// Wait for the child process to finish and get its status
let status = child.wait().expect("failed to wait on child");
println!("Process finished with: {}", status);
}
let res: Result<i32, &str> = Err("file not found");
match res {
Ok(val) => println!("Success: {}", val),
Err(e) => println!("Error: {}", e), // Shows the err message safely
}
let res: Result<i32, &str> = Err("bad input");
if let Err(e) = res {
println!("Error: {}", e);
}
fn main() {
let numbers = vec![1, 2, 3];
// .map() is safe and lazy; it transforms elements one by one
let doubled: Vec<i32> = numbers
.iter()
.map(|x| x * 2)
.collect();
println!("{:?}", doubled); // Output: [2, 4, 6]
}
fn main() {
let maybe_number: Option<i32> = Some(5);
// Transforms the value inside Some, or safely returns None
let doubled_option = maybe_number.map(|x| x * 2);
println!("{:?}", doubled_option); // Output: Some(10)
let empty: Option<i32> = None;
println!("{:?}", empty.map(|x| x * 2)); // Output: None (no panic!)
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
// .filter() takes a reference to the item (&&x)
// We dereference it (*x) to check the value
let evens: Vec<i32> = numbers
.into_iter()
.filter(|x| x % 2 == 0)
.collect();
println!("{:?}", evens); // Output: [2, 4, 6]
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let doubled_evens: Vec<i32> = numbers
.into_iter()
.filter(|x| x % 2 == 0) // Keeps: 2, 4, 6
.map(|x| x * 2) // Becomes: 4, 8, 12
.collect();
println!("{:?}", doubled_evens); // Output: [4, 8, 12]
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// Using .sum() on an iterator
let total: i32 = numbers.iter().sum();
println!("Sum is: {}", total); // Sum is: 15
}
fn main() {
// Using turbofish syntax to specify the output type
let sum: u32 = (1..=5).sum();
println!("Sum of range: {}", sum); // Sum of range: 15
}
fn main() {
// 1. Parsing a string into an integer
// Without turbofish, Rust doesn't know if you want a u32, i32, or f64
let number = "42".parse::<i32>().unwrap();
println!("Parsed number: {}", number);
// 2. Collecting a iterator into a specific collection
// Tells the compiler to group these numbers into a Vector
let micro_animals = vec!["tardigrade", "nematode"];
let animal_list = micro_animals.iter().collect::<Vec<&&str>>();
println!("Animal list: {:?}", animal_list);
}
let score: i32 = 100; // 32-bit signed integer
let pi: f64 = 3.14159; // 64-bit floating point
let is_active: bool = true; // Boolean
let greeting: &str = "Hello, Rust!"; // String slice
fn add_numbers(x: i32, y: i32) -> i32 {
x + y // Return type is annotated after the '->'
}
struct User {
username: String,
login_count: u64,
is_active: bool,
}
const MAX_POINTS: u32 = 100_000;
In Rust, the word static is used for three main things: declaring global variables with fixed memory locations, specifying a lifetime where data lives for the entire program, and defining trait bounds to ensure types do not contain temporary borrowed references.
- .filter_map(): Runs a function that returns an Option, keeping only the Some values and unwrapping them at the same time.
- .enumerate(): Yields pairs of (index, element) as you loop through items.
- .take(n): Keeps only the first n items from the sequence.
- .skip(n): Bypasses the first n items and yields the rest.
- .zip(other): Blends two streams into pairs (a, b).
- .flatten(): Flattens nested collections (like a Vec<Vec>) into a single level.
- .collect(): Gathers items back into a collection like a Vec or HashMap.
- .fold(init, f): Accumulates a single final value by carrying an intermediate state through a closure. Aka
reduce - .any(predicate): Returns true if any element matches the condition.
- .all(predicate): Returns true if every element matches the condition.
- .find(predicate): Returns the first item that matches the condition as an Option.
- .count(): Counts how many items are left in the iterator.
fn main() {
// 1. Summing a list of numbers
let numbers = vec![1, 2, 3, 4, 5];
let sum = numbers.iter().fold(0, |accumulator, &item| {
accumulator + item
});
println!("The sum is: {}", sum); // Output: 15
// 2. Building a string from a vector of words
let words = vec!["Rust", "is", "fast", "and", "safe"];
let sentence = words.iter().fold(String::new(), |mut acc, &word| {
if !acc.is_empty() {
acc.push(' ');
}
acc.push_str(word);
acc
});
println!("{}", sentence); // Output: Rust is fast and safe
}
use std::io;
fn main() {
println!("Please enter some text:");
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line"); // Handles potential I/O errors
// Note: read_line includes the trailing newline character '\n'
println!("You typed: {}", input.trim());
}
use std::process::Command;
use std::os::unix::process::CommandExt;
fn main() {
// This will completely replace your program with "echo"
let error = Command::new("echo")
.arg("Hello from the other side!")
.exec();
// This line only runs if exec fails (e.g., command not found)
println!("Error running exec: {}", error);
}
In Rust, `Rc` stands for `Reference Counted`.
It is a smart pointer type (Rc<T>) that enables multiple parts of your program to
share ownership of the same data on the heap within a single thread.
It tracks how many owners exist and deletes the data when that count hits zero.
In Rust, `Arc` stands for `Atomically Reference Counted`.
It is a thread-safe smart pointer that enables shared ownership of a value allocated on the heap.
Normally, Rust's strict ownership model dictates that a value can only have one owner at a time.
Arc bypasses this restriction safely in `multithreaded` environments by tracking
how many references to the data exist.
In Rust, Cow stands for Clone-on-Write (found in std::borrow::Cow).
It is a smart pointer that acts like a wrapper for data.
It lets you handle situations where data might be borrowed or owned, and it only clones (copies)
the data into a new, owned memory block if you actually try to change it.
Rust's std::thread::spawn uses the move keyword because new threads can outlive the current function.
A move closure takes full ownership of variables it captures from outside,
preventing dangling references if the parent function finishes before the spawned thread.
use std::thread;
fn main() {
let handle = thread::spawn(|| {
"Thread work completed!"
});
match handle.join() {
Ok(result) => println!("Success! Thread returned: {}", result),
Err(_) => println!("The spawned thread panicked!"),
}
}
An arithmetic progression (AP) is a sequence where the difference between consecutive terms is constant. In Rust, you can generate an AP using standard iterators, calculate the n-th term, or check if a sequence forms an AP using basic loops or specialized crates like use-series.
fn generate_ap(first: i32, diff: i32, count: usize) -> Vec<i32> {
std::iter::successors(Some(first), move |&prev| Some(prev + diff))
.take(count)
.collect()
}
fn main() {
let ap = generate_ap(2, 3, 5); // Starts at 2, step 3, 5 elements
println!("{:?}", ap); // Output: [2, 5, 8, 11, 14]
}
To overcome a value move in Rust, you can use borrowing (& or &mut), implement the Copy trait, explicitly clone the data, or use shared ownership smart pointers like Rc or Arc.
Instead of transferring ownership, pass a reference so the original variable remains valid and usable.
fn print_text(s: &String) {
println!("{}", s);
}
fn main() {
let s1 = String::from("hello");
print_text(&s1); // Borrowing s1 instead of moving it
println!("{}", s1); // Still valid here!
}
Behind the scenes in Rust, borrowing is a compile-time check with zero cost at runtime. The compiler uses a system called the borrow checker and lifetimes to track how long references are valid. This prevents data races and crashes before your code ever runs.
Types that have fixed size stored on the stack (like numbers) implement the Copy trait. Instead of moving, Rust automatically copies them. You can derive Copy for simple custom structs.
#[derive(Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // Copied, not moved!
println!("p1 is still valid: {}", p1.x);
}
If you need absolute ownership of a new variable and cannot share references, create a complete duplicate using .clone().
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // Deep copy of the heap data
println!("s1: {}, s2: {}", s1, s2); // Both are valid
}
When multiple parts of your program need joint ownership of data, wrap it in a Reference Counted smart pointer (Rc for single-threaded, Arc for multi-threaded).
use std::rc::Rc;
fn main() {
let s1 = Rc::new(String::from("hello"));
let s2 = Rc::clone(&s1); // Increments reference count
println!("Strong count: {}", Rc::strong_count(&s1));
}
use std::sync::Arc;
use std::thread;
use std::time::Duration;
// Shared configuration for our application
struct AppConfig {
app_name: String,
timeout_seconds: u64,
}
fn main() {
// Wrap the config in an Arc so it can be safely shared across threads
let config = Arc::new(AppConfig {
app_name: String::from("FastServer"),
timeout_seconds: 5,
});
let mut handles = vec![];
// Spawn 3 worker threads
for i in 1..=3 {
// Clone the Arc pointer for this thread
// This increments the reference count atomically, not the underlying data
let config_clone = Arc::clone(&config);
let handle = thread::spawn(move || {
// Access shared data safely from the new thread
println!(
"Worker {} started for {}. Timeout is {}s.",
i, config_clone.app_name, config_clone.timeout_seconds
);
thread::sleep(Duration::from_millis(100));
println!("Worker {} finished.", i);
});
handles.push(handle);
}
// Wait for all worker threads to finish
for handle in handles {
handle.join().unwrap();
}
println!("All workers done. Program exiting.");
}
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
// The data structure we want to share and mutate across threads
#[derive(Debug)]
struct JobStatus {
progress: u32,
state: &'static str,
}
fn main() {
// 1. Initialize the tracker wrapped in a Mutex, then an Arc
let job_tracker: Arc<Mutex<HashMap<String, JobStatus>>> = Arc::new(Mutex::new(HashMap::new()));
let mut thread_handles = vec![];
// 2. Spawn 3 background worker threads
for i in 1..=3 {
// Clone the Arc pointer for the new thread (increases reference count)
let tracker_clone = Arc::clone(&job_tracker);
let job_id = format!("job_{}", i);
let handle = thread::spawn(move || {
// Initialize the job status in the map
{
// lock() blocks until this thread exclusively owns the data
let mut map = tracker_clone.lock().unwrap();
map.insert(job_id.clone(), JobStatus { progress: 0, state: "Pending" });
// The lock is released here automatically when `map` goes out of scope
}
// Simulate background work increments
for step in 1..=5 {
thread::sleep(Duration::from_millis(100)); // Simulate time-consuming work
// Acquire the lock to update progress
let mut map = tracker_clone.lock().unwrap();
if let Some(job) = map.get_mut(&job_id) {
job.progress = step * 20;
job.state = if step == 5 { "Completed" } else { "Running" };
}
}
});
thread_handles.push(handle);
}
// 3. Simultaneously, simulate the main thread acts like an API reading the status
for _ in 0..3 {
thread::sleep(Duration::from_millis(150));
// Acquire lock just to read the current state
let map = job_tracker.lock().unwrap();
println!("--- Live API Snapshot ---");
for (id, status) in map.iter() {
println!("{}: {}% ({})", id, status.progress, status.state);
}
}
// 4. Ensure all worker threads complete their execution cleanly
for handle in thread_handles {
handle.join().unwrap();
}
// Print final report
println!("\nFinal State: {:?}", job_tracker.lock().unwrap());
}
Cargo.toml
[package]
name = "mk"
version = "0.1.0"
edition = "2024"
[dependencies]
k8s-openapi = { version = "0.28.0", features = ["v1_36"] }
kube = "4.2.0"
tokio = { version = "1.53.1", features = ["full"] }
src/main.rs
use kube::{Client, Api};
use k8s_openapi::api::core::v1::Pod;
#[tokio::main]
async fn main() -> Result<(), kube::Error> {
// Initialize the Kubernetes client from the default ~/.kube/config (Minikube context)
let client = Client::try_default().await?;
// Define an API accessor for Pods in the "default" namespace
let pods: Api<Pod> = Api::namespaced(client, "default");
// List the pods currently running in Minikube
for p in pods.list(&Default::default()).await? {
println!("Pod Name: {}", p.metadata.name.unwrap_or_default());
}
Ok(())
}
Cargo.toml
[package]
name = "lcpus"
version = "0.1.0"
edition = "2024"
[dependencies]
num_cpus = "1.17.0"
src/main.rs
fn main() {
let cpus = num_cpus::get();
println!("cpus: {cpus}");
}
Cargo.toml
[package]
name = "ram"
version = "0.1.0"
edition = "2024"
[dependencies]
sysinfo = "0.39.6"
src/main.rs
use sysinfo::System;
fn main() {
// Create and load system information
let mut sys = System::new_all();
// Refresh memory components
sys.refresh_memory();
// Get total and used memory in bytes
let total_ram = sys.total_memory();
let used_ram = sys.used_memory();
println!("Total RAM: {} bytes", total_ram);
println!("Used RAM: {} bytes", used_ram);
// Convert bytes to gigabytes (GB)
let gb = 1024 * 1024 * 1024;
println!("Total RAM: {} GB", total_ram / gb);
}