Introduction
Rust has been Stack Overflow's most loved language for nine consecutive years (2016–2024) and is now used by 85% of Fortune 500 companies for performance-critical systems (source: Rust Foundation 2025 survey). Whether you're building a zero-cost abstraction CLI utility, a memory-safe system service, or compiling Rust to WebAssembly for the browser, the right prompt can cut your development time in half.
This collection of 12 prompts is designed for Rust developers who want to move from “it compiles” to “it’s production-ready.” You’ll find practical, battle-tested prompts for safe concurrency, error handling, CLI argument parsing, WebAssembly interop, and even embedded Rust patterns. Each prompt includes a realistic scenario, the exact prompt text, and a code example you can adapt.
1. Safe Concurrency with std::sync::Arc and Mutex
Scenario: You have a shared counter that multiple threads increment. Without proper synchronization, you get data races. Rust’s type system prevents undefined behavior, but you need a correct pattern.
Prompt:
"Generate a Rust function that creates a shared mutable counter using
Arc<Mutex<u64>>. Spawn 10 threads, each incrementing the counter 1,000 times. After all threads join, print the final count. Usestd::sync::mpscfor logging each increment in a separate thread."
Example Result:
use std::sync::{Arc, Mutex, mpsc};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0u64));
let (tx, rx) = mpsc::channel();
let mut handles = vec![];
for i in 0..10 {
let counter = Arc::clone(&counter);
let tx = tx.clone();
handles.push(thread::spawn(move || {
for _ in 0..1000 {
let mut val = counter.lock().unwrap();
*val += 1;
tx.send(format!("Thread {} incremented to {}", i, *val)).unwrap();
}
}));
}
for handle in handles {
handle.join().unwrap();
}
drop(tx);
for msg in rx {
println!("{}", msg);
}
println!("Final counter: {}", *counter.lock().unwrap());
}
Key takeaway: Arc<Mutex<T>> is the standard approach for shared mutable state across threads. The prompt forces you to combine two sync primitives safely.
2. Zero-Cost CLI Argument Parsing with clap
Scenario: You need a CLI tool that accepts a file path (-f), an optional output format (--format), and a verbosity flag (-v). Hand-parsing is error-prone.
Prompt:
"Write a Rust CLI using
clapv4 (derive macro) that accepts-f <path>,--format <json|yaml>(defaultjson), and-vfor verbose mode. Print parsed values. Useclap::Parserderive."
Example Result:
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about = "My CLI tool")]
struct Args {
/// Input file path
#[arg(short = 'f', long = "file", required = true)]
file: String,
/// Output format
#[arg(long = "format", default_value = "json")]
format: String,
/// Verbose mode
#[arg(short, long)]
verbose: bool,
}
fn main() {
let args = Args::parse();
println!("File: {}", args.file);
println!("Format: {}", args.format);
if args.verbose {
println!("Verbose mode on");
}
}
Key takeaway: clap’s derive API gives you zero-cost abstraction—no runtime overhead for parsing. The prompt ensures you cover required vs optional arguments.
3. Compile Rust to WebAssembly with wasm-pack
Scenario: You have a Rust library that performs heavy numeric computations. You want to call it from JavaScript in the browser.
Prompt:
"Create a Rust function
fibonacci(n: u32) -> u64that computes the nth Fibonacci number using an iterative approach. Export it to WebAssembly usingwasm-bindgen. Write a JavaScript snippet that calls this function and logs the result."
Example Result:
// lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => {
let mut a = 0;
let mut b = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
}
}
// index.js
import init, { fibonacci } from './pkg/my_wasm.js';
async function run() {
await init();
console.log(fibonacci(30)); // 832040
}
run();
Key takeaway: wasm-bindgen handles serialization and memory management automatically. The prompt forces you to think about the interface boundary: only Copy types or JsValue can cross the bridge.
4. Error Handling with thiserror and anyhow
Scenario: Your application has multiple error types (IO, parsing, network). You need a unified error type for main() that preserves context.
Prompt:
"Define a custom error enum using
thiserrorfor a file-reading application that can fail withIoError,ParseError(String), andNetworkError. Then write a functionread_config(path: &str) -> Result<String, AppError>that opens a file and returns its content, using?to propagate errors. Useanyhow::Resultin main()."
Example Result:
use thiserror::Error;
use std::fs::File;
use std::io::Read;
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(String),
#[error("Network error: {0}")]
Network(String),
}
fn read_config(path: &str) -> Result<String, AppError> {
let mut file = File::open(path)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
fn main() -> anyhow::Result<()> {
let config = read_config("config.toml")?;
println!("{}", config);
Ok(())
}
Key takeaway: thiserror reduces boilerplate for library errors; anyhow is for application-level error handling where you just want to ? and print.
5. Parsing JSON with serde_json and Struct Derivation
Scenario: You have a JSON API response with nested fields. You need to deserialize it into a Rust struct.
Prompt:
"Given the JSON:
{\"user\":{\"id\":42,\"name\":\"Alice\"},\"status\":\"ok\"}, define a Rust struct usingserde::Deserializethat captures the full structure. Write a functionparse_data(json: &str) -> Result<Response, serde_json::Error>that deserializes and prints the user's name."
Example Result:
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct User {
id: u32,
name: String,
}
#[derive(Deserialize, Debug)]
struct Response {
user: User,
status: String,
}
fn parse_data(json: &str) -> Result<Response, serde_json::Error> {
let resp: Response = serde_json::from_str(json)?;
println!("User name: {}", resp.user.name);
Ok(resp)
}
fn main() {
let data = r#"{"user":{"id":42,"name":"Alice"},"status":"ok"}"#;
parse_data(data).unwrap();
}
Key takeaway: serde is the de facto standard for serialization in Rust. The prompt teaches nested struct derivation and error handling with ?.
6. Building a TCP Server with std::net
Scenario: You need a simple echo server that accepts connections and sends back whatever it receives.
Prompt:
"Write a Rust TCP server using
std::net::TcpListenerthat listens on127.0.0.1:8080. For each connection, spawn a new thread that reads data in chunks and writes it back. UseBufReadfor line-based reading. Handle graceful shutdown on Ctrl+C."
Example Result:
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;
fn handle_client(mut stream: TcpStream) {
let peer = stream.peer_addr().unwrap();
println!("New connection: {}", peer);
let mut reader = BufReader::new(stream.try_clone().unwrap());
for line in reader.lines() {
match line {
Ok(msg) => {
println!("Received: {}", msg);
if let Err(e) = writeln!(stream, "Echo: {}", msg) {
eprintln!("Write error: {}", e);
break;
}
}
Err(e) => {
eprintln!("Read error: {}", e);
break;
}
}
}
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
for stream in listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(|| handle_client(stream));
}
Err(e) => eprintln!("Connection failed: {}", e),
}
}
Ok(())
}
Key takeaway: Rust’s standard library provides low-level networking without external crates. The prompt introduces concurrency patterns and BufRead for efficient I/O.
7. Writing a Custom Iterator
Scenario: You have a sequence of Fibonacci numbers, but you want to generate them lazily without allocating a vector.
Prompt:
"Implement a
Fibonaccistruct that implementsIteratoryieldingu64values. The iterator should never overflow—useOption<u64>and returnNonewhen the next value exceedsu64::MAX. Test it by taking the first 10 values."
Example Result:
struct Fibonacci {
a: u64,
b: u64,
}
impl Fibonacci {
fn new() -> Self {
Fibonacci { a: 0, b: 1 }
}
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
let current = self.a;
self.a = self.b;
self.b = self.b.checked_add(current)?;
Some(current)
}
}
fn main() {
let fib = Fibonacci::new();
for (i, val) in fib.take(10).enumerate() {
println!("Fib {}: {}", i, val);
}
}
Key takeaway: Custom iterators let you express infinite sequences with zero heap allocation. checked_add prevents overflow, a safety pattern Rust enforces by default.
8. WebAssembly Interop with JavaScript Callbacks
Scenario: You want your Rust WASM module to call a JavaScript function (e.g., console.log).
Prompt:
"Write a Rust function
run_js_callbackthat accepts ajs_sys::Functionargument and calls it with a string\"Hello from Rust!\". Usewasm-bindgenandjs_sys. Then show the JavaScript code that passesconsole.logas the callback."
Example Result:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn run_js_callback(callback: &js_sys::Function) {
let msg = JsValue::from_str("Hello from Rust!");
let this = JsValue::null();
let _ = callback.call1(&this, &msg);
}
import init, { run_js_callback } from './pkg/my_wasm.js';
await init();
run_js_callback(console.log);
// Console: "Hello from Rust!"
Key takeaway: js_sys::Function allows two-way interop. The prompt teaches you to pass JavaScript functions into Rust WASM and invoke them safely.
9. Memory-Safe FFI with libc and std::ffi
Scenario: You need to call a C function int add(int a, int b) from Rust using FFI.
Prompt:
"Write a Rust program that links to an external C library
libaddcontainingint add(int a, int b). Useextern \"C\"andstd::ffi::c_int. Handle linking via a build script (build.rs). Print the result ofadd(3, 4)."
Example Result:
// build.rs
fn main() {
println!("cargo:rustc-link-lib=add");
}
// main.rs
use std::ffi::c_int;
extern "C" {
fn add(a: c_int, b: c_int) -> c_int;
}
fn main() {
unsafe {
let result = add(3, 4);
println!("3 + 4 = {}", result);
}
}
Key takeaway: FFI calls are inherently unsafe—Rust cannot verify C code’s memory safety. The prompt forces you to contain the unsafe block and use the correct integer types (c_int).
10. Embedded Rust: Blinking an LED on Raspberry Pi Pico
Scenario: You want to blink an LED on a Raspberry Pi Pico (RP2040) using Rust without the Arduino ecosystem.
Prompt:
"Write a Rust program for the Raspberry Pi Pico (RP2040) using
rp2040-halandcortex-m-rt. Configure GPIO pin 25 (built-in LED) as output, then blink it with a 500ms delay in an infinite loop. Useembedded_hal::digital::OutputPintrait."
Example Result:
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin;
use panic_halt as _;
use rp2040_hal::{clocks::Clock, pac, watchdog::Watchdog, Sio};
#[entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let sio = Sio::new(pac.SIO);
let clocks = rp2040_hal::clocks::init_clocks_and_plls(
rp2040_hal::fugit::RateExtU32::Hz(12_000_000),
&mut pac.XOSC,
&mut pac.CLOCKS,
&mut pac.PLL_SYS,
&mut pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let pins = rp2040_hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
let mut led_pin = pins.gpio25.into_push_pull_output();
let mut delay = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
loop {
led_pin.set_high().unwrap();
delay.delay_ms(500);
led_pin.set_low().unwrap();
delay.delay_ms(500);
}
}
Key takeaway: Embedded Rust uses the same ownership and trait system as desktop Rust. The prompt introduces #[entry], no_std, and hardware abstraction layers (HAL).
11. Asynchronous HTTP Client with reqwest
Scenario: You need to fetch data from a REST API concurrently without blocking threads.
Prompt:
"Write an async Rust function that uses
reqwestto fetch two URLs concurrently usingtokio::join!. Print the status codes and first 100 characters of each response body. Usetokioruntime."
Example Result:
use reqwest;
use tokio;
async fn fetch_url(url: &str) -> Result<(String, String), reqwest::Error> {
let resp = reqwest::get(url).await?;
let status = resp.status().to_string();
let body = resp.text().await?;
let snippet = body.chars().take(100).collect();
Ok((status, snippet))
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let url1 = "https://httpbin.org/get";
let url2 = "https://example.com";
let ((s1, b1), (s2, b2)) = tokio::join!(
fetch_url(url1),
fetch_url(url2)
);
println!("URL1: status={}, body={}", s1, b1);
println!("URL2: status={}, body={}", s2, b2);
Ok(())
}
Key takeaway: Async Rust with tokio is zero-cost; you don’t pay for concurrency you don’t use. join! runs futures concurrently, not in sequence.
12. File Watching with notify Crate
Scenario: You want a CLI tool that watches a directory and prints notifications when files are created, modified, or deleted.
Prompt:
"Write a Rust program using the
notifycrate that watches a given directory (passed as CLI argument). On any event, print the event type and path. UseRecursiveMode::Recursiveto watch subdirectories. Handle errors gracefully."
Example Result:
use notify::{Config, Event, RecursiveMode, Watcher};
use std::path::Path;
fn main() -> notify::Result<()> {
let args: Vec<String> = std::env::args().collect();
let path = if args.len() > 1 {
args[1].clone()
} else {
".".to_string()
};
let mut watcher = notify::recommended_watcher(
|res: Result<Event, notify::Error>| {
match res {
Ok(event) => println!("Event: {:?} on {:?}", event.kind, event.paths),
Err(e) => eprintln!("Watch error: {}", e),
}
})?;
watcher.watch(Path::new(&path), RecursiveMode::Recursive)?;
println!("Watching {}. Press Ctrl+C to stop.", path);
loop {
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
Key takeaway: The notify crate abstracts cross-platform file system events. The prompt teaches event-driven programming with a callback closure.
Conclusion
These 12 prompts cover the most common Rust development scenarios: safe concurrency, CLI tools, WebAssembly interop, error handling, serialization, networking, custom iterators, FFI, embedded systems, async HTTP, and file watching. Each prompt is designed to be a building block you can adapt to your own projects.
Rust's strength lies in its ability to guarantee memory safety without a garbage collector. By mastering these patterns, you’ll write code that is not only correct but also performant and maintainable. Try adapting one of these prompts to your current project today.
Comments