Every Cochran Block product (runsible, r8r, exopack, others in the Manual) is shipped as a single Rust binary. Most working developers don't write Rust. They write Python, JavaScript, Go, C, Java, C#, Ruby. The Rosetta is the translation between the language you already know and the language the railgun speaks.
It is not a complete reference. It is a working alternative to "I'd love to use that crate but I don't know Rust." Read one row, copy the right column into your editor, ship.
The first translation, because Michael's mental model is Python-procedural. Concurrency-focused, no OOP. Most rows here are 1-for-1.
| Pattern | Python | Rust |
|---|---|---|
| Print a line | print("hello, world") |
println!("hello, world"); |
| Variable (immutable by default in Rust) | name = "Michael" age = 38 |
let name = "Michael"; let age: u32 = 38; |
| Mutable variable | count = 0 count += 1 |
let mut count = 0; count += 1; |
| String formatting | msg = f"hi {name}, age {age}" |
let msg = format!("hi {name}, age {age}"); |
| List / Vec | xs = [1, 2, 3]
xs.append(4)
for x in xs:
print(x) |
let mut xs: Vec<i32> = vec![1, 2, 3];
xs.push(4);
for x in &xs {
println!("{x}");
} |
| Dict / HashMap | m = {"a": 1, "b": 2}
m["c"] = 3
for k, v in m.items():
print(k, v) |
use std::collections::HashMap;
let mut m: HashMap<&str, i32> = HashMap::new();
m.insert("a", 1);
m.insert("b", 2);
m.insert("c", 3);
for (k, v) in &m {
println!("{k} {v}");
} |
| Function | def add(a: int, b: int) -> int:
return a + b |
fn add(a: i32, b: i32) -> i32 {
a + b
} |
| Error handling | try:
result = risky()
except IOError as e:
print(f"oops: {e}") |
match risky() {
Ok(result) => { /* use result */ }
Err(e) => eprintln!("oops: {e}"),
}
// or with ?:
let result = risky()?; |
| Read a file | with open("notes.txt") as f:
text = f.read() |
let text = std::fs::read_to_string("notes.txt")?; |
| Write a file | with open("out.txt", "w") as f:
f.write("done\n") |
std::fs::write("out.txt", "done\n")?; |
| JSON parse | import json data = json.loads(s) name = data["name"] |
// Cargo.toml: serde_json = "1"
let data: serde_json::Value =
serde_json::from_str(&s)?;
let name = data["name"].as_str().unwrap(); |
| JSON struct (typed) | from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
import json
u = User(**json.loads(s)) |
// Cargo.toml: serde = { version="1", features=["derive"] }
// serde_json = "1"
use serde::Deserialize;
#[derive(Deserialize)]
struct User { name: String, age: u32 }
let u: User = serde_json::from_str(&s)?; |
| HTTP GET | import requests
r = requests.get("https://api.example.com/x")
data = r.json() |
// Cargo.toml: reqwest = { version="0.12", features=["json"] }
let data: serde_json::Value =
reqwest::blocking::get("https://api.example.com/x")?.json()?; |
| HTTP server (FastAPI ↔ axum) | from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def hello():
return {"msg": "hi"} |
// Cargo.toml: axum = "0.8", tokio = { version="1", features=["full"] }
use axum::{Router, routing::get, Json};
use serde_json::json;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/hello", get(|| async { Json(json!({"msg": "hi"})) }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
axum::serve(listener, app).await.unwrap();
} |
| Async coroutine | import asyncio
async def fetch(url):
await asyncio.sleep(1)
return url
asyncio.run(fetch("x")) |
// Cargo.toml: tokio = { version="1", features=["full"] }
async fn fetch(url: &str) -> &str {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
url
}
#[tokio::main]
async fn main() {
let _ = fetch("x").await;
} |
| Run a subprocess | import subprocess
out = subprocess.run(
["ls", "-la"],
capture_output=True, text=True
).stdout |
let out = std::process::Command::new("ls")
.arg("-la")
.output()?;
let stdout = String::from_utf8(out.stdout)?; |
| Regex | import re m = re.match(r"(\d+)-(\w+)", "42-foo") if m: print(m.group(1), m.group(2)) |
// Cargo.toml: regex = "1"
use regex::Regex;
let re = Regex::new(r"(\d+)-(\w+)")?;
if let Some(c) = re.captures("42-foo") {
println!("{} {}", &c[1], &c[2]);
} |
| CLI args | import argparse
p = argparse.ArgumentParser()
p.add_argument("--name", required=True)
args = p.parse_args()
print(args.name) |
// Cargo.toml: clap = { version="4", features=["derive"] }
use clap::Parser;
#[derive(Parser)]
struct Args { #[arg(long)] name: String }
fn main() {
let args = Args::parse();
println!("{}", args.name);
} |
| Test | def test_add():
assert add(2, 3) == 5
# pytest tests/test_x.py |
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
// cargo test |
For the systems crowd. Rust keeps everything C gives you (no GC, predictable layout, FFI), and adds the ownership rules that make refactoring safe.
| Pattern | C | Rust |
|---|---|---|
| Print a line | #include <stdio.h>
printf("hello, world\n"); |
println!("hello, world"); |
| Heap allocation | int *p = malloc(sizeof(int)); *p = 42; free(p); |
let p = Box::new(42_i32); // dropped automatically at end of scope |
| Pointer / reference | void incr(int *x) { (*x)++; }
int v = 1;
incr(&v); |
fn incr(x: &mut i32) { *x += 1; }
let mut v = 1;
incr(&mut v); |
| Strings | const char *name = "Michael";
char buf[64];
snprintf(buf, sizeof buf,
"hi %s", name); |
let name = "Michael";
let buf = format!("hi {name}"); |
| Struct | struct Point { float x, y; };
struct Point p = { .x = 1.0, .y = 2.0 }; |
struct Point { x: f32, y: f32 }
let p = Point { x: 1.0, y: 2.0 }; |
| Enum / tagged union | enum Shape { CIRCLE, SQUARE };
struct ShapeVal {
enum Shape kind;
union { float r; float side; };
}; |
enum Shape {
Circle { r: f32 },
Square { side: f32 },
} |
| Read a file | FILE *f = fopen("notes.txt", "r");
char buf[1024];
size_t n = fread(buf, 1, 1023, f);
buf[n] = '\0';
fclose(f); |
let text = std::fs::read_to_string("notes.txt")?; |
| Dynamic array | int *xs = malloc(4 * sizeof(int)); xs[0] = 1; xs[1] = 2; xs[2] = 3; xs[3] = 4; // realloc, track length, free, etc. free(xs); |
let xs = vec![1, 2, 3, 4]; // length tracked, dropped automatically |
| Error handling | int rc = do_thing();
if (rc != 0) {
fprintf(stderr, "fail: %s\n", strerror(errno));
return rc;
} |
do_thing()?;
// or with full match:
match do_thing() {
Ok(_) => {},
Err(e) => eprintln!("fail: {e}"),
} |
| Function pointer / closure | typedef int (*Fn)(int);
int apply(int x, Fn f) { return f(x); }
int sq(int x) { return x * x; }
int y = apply(3, sq); |
fn apply<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 { f(x) }
let y = apply(3, |n| n * n); |
| Build & run | cc -O2 -o app main.c ./app |
cargo run --release |
| Static link to libc / FFI | // you ARE libc extern int getpid(void); |
extern "C" { fn getpid() -> i32; }
unsafe { let _ = getpid(); } |
Box, Vec, String, Result, and the borrow checker take over the work that used to be malloc/realloc/free + length tracking + error codes + segfault hunts. Most C programs become shorter in Rust.
Translations are added in a deliberate order: Python first (above), then C (above), then Go, JavaScript / TypeScript, Java / Kotlin, C++, C#, Ruby, Bash. Each language gets the same row pattern so your eye learns the shape once.
Source for this page lives in the cochranblock crate at github.com/cochranblock/cochranblock. Pull requests adding rows or whole languages are welcome and will be folded into the next manual revision.