Introduction
Rust has matured into a language that dominates systems programming, command-line tooling, and WebAssembly (Wasm). Its unique ownership model and zero-cost abstractions make it ideal for writing safe, high-performance code. But even experienced developers can spend hours debugging borrow checker errors or optimizing memory layouts. That's where targeted prompts come in.
Over the past year, I've distilled a set of battle-tested prompts that accelerate Rust development — from parsing complex data formats to building CLI utilities with clap and crafting Wasm modules that integrate with JavaScript. Each prompt is designed to be dropped into your editor, ChatGPT, or local LLM to get immediate, actionable Rust code.
This article collects 20 prompts organized by domain: systems programming (memory safety, error handling, concurrency), CLI development (argument parsing, subcommands, output formatting), and WebAssembly (bindings, DOM manipulation, performance). Every prompt includes a real-world example and notes on where it fits in your workflow.
Systems Programming Prompts
1. Safe FFI Binding Generator
Prompt: "Generate a Rust unsafe FFI binding for a C function that takes a char* and returns int. Use std::ffi::CStr and handle null pointers. Include error handling for invalid UTF-8."
Example: Imagine wrapping a C library that validates file checksums. The prompt produces a binding that safely converts C strings to Rust slices, checks for null, and returns Result<i32, FfiError>.
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
pub fn safe_call_c_function(input: &str) -> Result<i32, String> {
let c_input = CString::new(input).map_err(
|e| format!("Null in string: {}", e))?;
let result = unsafe { c_function(c_input.as_ptr()) };
Ok(result)
}
extern "C" {
fn c_function(input: *const c_char) -> i32;
}
2. Zero-Copy Parsing with nom
Prompt: "Write a nom parser for a custom binary protocol with a 4-byte length prefix and a payload of that length. Return the payload as a &[u8] slice without allocation."
Example: Parsing network packets in a real-time telemetry system. The prompt yields a parser that uses nom's take combinator, ensuring no heap allocation for the payload.
use nom::{bytes::complete::take, number::complete::le_u32, IResult};
fn parse_packet(input: &[u8]) -> IResult<&[u8], &[u8]> {
let (input, length) = le_u32(input)?;
let (input, payload) = take(length)(input)?;
Ok((input, payload))
}
3. Custom Allocator for Embedded Systems
Prompt: "Implement a simple bump allocator for Rust that uses a fixed-size static buffer. Include alloc, dealloc, and realloc methods. Ensure it's safe to use with #[global_allocator]."
Example: Running Rust on a microcontroller with 64 KB RAM. The prompt produces a bump allocator that never frees memory, suitable for short-lived tasks.
use std::alloc::{GlobalAlloc, Layout};
use std::cell::UnsafeCell;
struct BumpAllocator {
heap: UnsafeCell<[u8; 65536]>,
offset: UnsafeCell<usize>,
}
unsafe impl GlobalAlloc for BumpAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let off = *self.offset.get();
let new_off = off + layout.size();
if new_off > 65536 {
return std::ptr::null_mut();
}
*self.offset.get() = new_off;
(*self.heap.get()).as_mut_ptr().add(off)
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}
4. Concurrent Work Queue with Crossbeam
Prompt: "Build a multi-producer, multi-consumer work queue in Rust using crossbeam::channel. Workers process tasks in parallel and send results back via another channel. Include graceful shutdown."
Example: A log analysis tool that processes thousands of files concurrently. The prompt generates code where workers read lines, match patterns, and report results to a collector.
use crossbeam::channel::{bounded, Receiver, Sender};
use std::thread;
fn main() {
let (tx_in, rx_in) = bounded::<String>(100);
let (tx_out, rx_out) = bounded::<String>(100);
let workers: Vec<_> = (0..4).map(
|_| {
let rx = rx_in.clone();
let tx = tx_out.clone();
thread::spawn(move || {
while let Ok(task) = rx.recv() {
let result = format!("Processed: {}", task.len());
tx.send(result).unwrap();
}
})
}).collect();
// Send tasks and collect results...
}
5. Unsafe Code Audit Checklist
Prompt: "List the top 10 safety checks I should perform when reviewing unsafe Rust code. Include examples of common violations like dangling pointers and uninitialized memory."
Example: A code review checklist used in a systems team at a financial exchange. The prompt yields a structured list with code snippets showing mistakes and fixes.
| Check | Description |
|---|---|
| 1 | Ensure raw pointers are derived from valid references |
| 2 | Verify that UnsafeCell is used for interior mutability |
| 3 | Check that extern functions are correctly declared |
| 4 | Validate that std::mem::uninitialized is replaced with MaybeUninit |
| 5 | Confirm that drop is called exactly once for each allocation |
CLI Tool Prompts
6. Minimal Argument Parser with clap
Prompt: "Create a CLI tool using clap v4 that accepts a file path (-f), an optional output format (--format with values json/yaml), and a verbosity flag (-v). Generate the struct with #[derive(Parser)]."
Example: A configuration file converter. The prompt generates a struct with derive macros and a main that parses args and dispatches.
use clap::Parser;
#[derive(Parser)]
#[command(name = "config-tool")]
struct Cli {
#[arg(short = 'f', long)]
file: String,
#[arg(long, default_value = "json")]
format: String,
#[arg(short, long, action = clap::ArgAction::Count)]
verbose: u8,
}
fn main() {
let cli = Cli::parse();
println!("File: {}, Format: {}, Verbose: {}", cli.file, cli.format, cli.verbose);
}
7. Colored Output with ansi_term
Prompt: "Write a Rust CLI that prints a success message in green, a warning in yellow, and an error in red using the ansi_term crate. Use style chaining for bold."
Example: A build tool that shows colored compilation status. The prompt yields a helper function.
use ansi_term::Colour::{Green, Yellow, Red};
fn print_status(status: &str, message: &str) {
match status {
"success" => println!("{}", Green.bold().paint(message)),
"warning" => println!("{}", Yellow.paint(message)),
"error" => println!("{}", Red.bold().paint(message)),
_ => println!("{}", message),
}
}
8. Progress Bar with indicatif
Prompt: "Implement a progress bar in Rust that tracks downloading 100 files. Use indicatif with a spinner and ETA. Update the bar after each file completes."
Example: A bulk file downloader. The prompt builds a ProgressBar that increments after each file, showing elapsed time and speed.
use indicatif::{ProgressBar, ProgressStyle};
use std::thread;
use std::time::Duration;
fn main() {
let pb = ProgressBar::new(100);
pb.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")
.unwrap());
for _ in 0..100 {
thread::sleep(Duration::from_millis(50));
pb.inc(1);
}
pb.finish_with_message("done");
}
9. Configuration File Reader
Prompt: "Write a Rust function that reads a TOML config file and returns a struct with fields: host (string), port (u16), and debug (bool). Use serde for deserialization. Handle file-not-found errors gracefully."
Example: A database connection config for a maintenance CLI. The prompt produces a Config struct with #[derive(Deserialize)] and a loader that returns Result<Config, ConfigError>.
use serde::Deserialize;
use std::fs;
#[derive(Deserialize)]
struct Config {
host: String,
port: u16,
debug: bool,
}
fn load_config(path: &str) -> Result<Config, Box<dyn std::error::Error>> {
let content = fs::read_to_string(path)?;
let config: Config = toml::from_str(&content)?;
Ok(config)
}
10. Signal Handling for Graceful Shutdown
Prompt: "Implement graceful shutdown for a Rust CLI that listens to SIGINT and SIGTERM using ctrlc and signal-hook. Print a message and exit cleanly."
Example: A long-running data pipeline that needs to flush buffers on exit. The prompt sets up a channel to receive OS signals and a handler that cleans up.
use ctrlc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
fn main() {
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
println!("Shutting down...");
}).expect("Error setting handler");
while running.load(Ordering::SeqCst) {
// work
}
}
WebAssembly Prompts
11. Basic Wasm Module with wasm-pack
Prompt: "Create a Rust function add(a: i32, b: i32) -> i32 and export it to WebAssembly. Use wasm-bindgen for the export. Show the full Cargo.toml and lib.rs."
Example: A math utility for a browser-based calculator. The prompt produces a minimal project that compiles with wasm-pack build --target web.
// Cargo.toml
// [lib]
// crate-type = ["cdylib"]
// [dependencies]
// wasm-bindgen = "0.2"
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
12. DOM Manipulation from Rust
Prompt: "Write a Rust function that creates a <div> element with text content and appends it to the document body. Use web-sys and wasm-bindgen. Include error handling for missing DOM API."
Example: A component that renders a status message in a single-page app. The prompt generates code that accesses window.document and manipulates the DOM.
use wasm_bindgen::prelude::*;
use web_sys::{Document, Element};
#[wasm_bindgen]
pub fn add_element(text: &str) -> Result<(), JsValue> {
let document = web_sys::window().unwrap().document().unwrap();
let div = document.create_element("div")?;
div.set_text_content(Some(text));
document.body().unwrap().append_child(&div)?;
Ok(())
}
13. Callback from JavaScript to Rust
Prompt: "Create a Rust function that accepts a JavaScript callback and invokes it with a string parameter. Use Closure to avoid memory leaks."
Example: A Wasm module that processes data and calls a JS function to update a chart. The prompt shows how to wrap a closure and keep it alive.
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
#[wasm_bindgen]
pub fn register_callback(cb: js_sys::Function) {
let closure = Closure::wrap(Box::new(move || {
let _ = cb.call1(&JsValue::NULL, &JsValue::from_str("Hello from Rust!"));
}) as Box<dyn Fn()>);
closure.forget(); // prevent drop
}
14. Performance-Critical Loop with Wasm
Prompt: "Implement a Rust function that computes a dot product of two f64 slices. Use iterators and avoid bounds checks. Export to Wasm with #[wasm_bindgen]. Compare performance with JS."
Example: A scientific simulation that runs numeric computations in the browser. The prompt yields a loop that is 2-5x faster than equivalent JS.
#[wasm_bindgen]
pub fn dot_product(a: &[f64], b: &[f64]) -> f64 {
a.iter().zip(b.iter()).map(
|(x, y)| x * y).sum()
}
15. WebAssembly Memory Sharing
Prompt: "Write Rust code that allocates a buffer, fills it with data, and returns a pointer and length to JavaScript. Use wasm_bindgen's Box<[u8]> conversion."
Example: A Wasm module that generates audio samples and passes them to JS for playback. The prompt returns a Vec<u8> which is automatically converted to a typed array.
#[wasm_bindgen]
pub fn generate_buffer() -> Box<[u8]> {
(0..1024).map(
|i| (i % 256) as u8).collect::<Vec<_>>().into_boxed_slice()
}
Advanced and Niche Prompts
16. Procedural Macro for derive(Error)
Prompt: "Create a custom derive macro #[derive(MyError)] that generates Display and Debug for an error enum. Use syn and quote."
Example: Internal library that replaces thiserror with a smaller custom implementation. The prompt generates the macro logic.
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(MyError)]
pub fn my_error_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let gen = quote! {
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
};
gen.into()
}
17. Async HTTP Client with reqwest
Prompt: "Write an async Rust function that fetches JSON from a URL, deserializes it into a struct, and handles timeout and connection errors. Use reqwest and tokio."
Example: A CLI that queries a REST API for weather data. The prompt builds a fetch_weather function that returns Result<Weather, ReqwestError>.
use reqwest::Client;
use serde::Deserialize;
#[derive(Deserialize)]
struct Weather {
temp: f64,
humidity: u8,
}
async fn fetch_weather(url: &str) -> Result<Weather, reqwest::Error> {
let client = Client::new();
let resp = client.get(url).timeout(std::time::Duration::from_secs(5)).send().await?;
let weather: Weather = resp.json().await?;
Ok(weather)
}
18. Memory Profiling with alloc
Prompt: "Write a Rust program that uses #[global_allocator] with a custom allocator that logs all allocations and deallocations. Use std::alloc::System as fallback."
Example: Debugging memory leaks in a server application. The prompt outputs a wrapper allocator that prints each alloc and dealloc with size.
use std::alloc::{GlobalAlloc, Layout, System};
struct LoggingAllocator;
unsafe impl GlobalAlloc for LoggingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = System.alloc(layout);
eprintln!("ALLOC: {:?} bytes at {:?}", layout.size(), ptr);
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
eprintln!("DEALLOC: {:?} bytes at {:?}", layout.size(), ptr);
System.dealloc(ptr, layout);
}
}
#[global_allocator]
static A: LoggingAllocator = LoggingAllocator;
19. Const Generics for Fixed-Size Arrays
Prompt: "Write a Rust function that takes a fixed-size array [u8; N] and returns it reversed. Use const generics. Ensure it works for any N."
Example: A network protocol parser that operates on fixed-length headers. The prompt avoids dynamic allocation.
fn reverse_array<const N: usize>(mut arr: [u8; N]) -> [u8; N] {
let mut i = 0;
let mut j = N - 1;
while i < j {
arr.swap(i, j);
i += 1;
j -= 1;
}
arr
}
20. Building a Minimal WebSocket Server
Prompt: "Implement a simple WebSocket server in Rust using tokio-tungstenite. Accept connections, echo messages back, and handle disconnects."
Example: A real-time collaboration tool prototype. The prompt produces an async server that listens on a port and echoes.
use tokio::net::TcpListener;
use tokio_tungstenite::accept_async;
#[tokio::main]
async fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
while let Ok((stream, _)) = listener.accept().await {
tokio::spawn(async move {
let ws_stream = accept_async(stream).await.unwrap();
let (write, read) = ws_stream.split();
read.forward(write).await.unwrap();
});
}
}
Conclusion
These 20 prompts cover the most common Rust tasks I encounter daily — from writing safe FFI bindings to deploying Wasm modules that interact with the DOM. Each prompt is a starting point: adapt the generics, error handling, and async patterns to your specific domain.
For teams building production Rust systems, ASI Biont supports connecting custom CLI tools and Wasm modules to enterprise workflows via API — see details at asibiont.com/courses. The key is to automate the repetitive parts of Rust development without sacrificing safety. Use these prompts as templates, and always review generated unsafe blocks with the checklist from prompt 5.
Comments