0.3 The Go ↔ C ↔ Rust phrasebook
Use this table when you know how to say something in Go or C and want the Rust equivalent. Every row is explained properly later in the guide.
| Idea | Go | C | Rust |
|---|---|---|---|
| Who frees memory | the GC, eventually | you, with free, exactly once |
the owner, when it goes out of scope (Drop), decided at compile time |
| Heap allocation | new(T), &T{}, escape analysis |
malloc(sizeof(T)) |
Box::new(t), Vec, String |
| Pointer that may be null | *T (nil) |
T * (NULL) |
Option<&T> / Option<Box<T>>. A plain &T is never null |
| Read-only view | by convention | const T * |
&T (shared borrow) |
| Exclusive writable access | by convention | by convention | &mut T (unique borrow, checked) |
| Growable array | []T + append |
hand-rolled vector | Vec<T> + push |
| View into an array | slice s[a:b] (can alias and grow) |
T * + length |
&v[a..b], a fat pointer that cannot outlive or alias-write v |
| String | string (immutable bytes, UTF-8 by convention) |
char *, NUL-terminated |
String (owned) / &str (borrowed), always valid UTF-8 |
| Map | map[K]V |
hand-rolled hash table | HashMap<K, V>, BTreeMap<K, V> |
| Error | (v, err), if err != nil |
return code + errno |
Result<T, E> + ? |
| Unrecoverable failure | panic |
abort(), or UB |
panic! (unwinds by default) |
| Sum type / tagged union | interface + type switch | struct { enum tag; union {...} } |
enum with data + match (exhaustive) |
| Polymorphism | interfaces (implicit) | function pointers, vtables by hand | traits: generics (static) or dyn Trait (vtable) |
| Generics | type parameters (GC shape stenciling) | macros / void * |
generics, monomorphized like C++ templates |
| Cleanup | defer |
goto cleanup |
Drop, runs automatically, including on early return |
| Threads | goroutines (M:N, runtime) | pthread_create |
std::thread (1:1 OS threads) |
| Lightweight tasks | goroutines | — (event loop by hand, epoll) |
async + an executor such as Tokio |
| Channel | chan T |
pipe or hand-rolled queue | std::sync::mpsc, tokio::sync::mpsc |
| Mutex | sync.Mutex, guards nothing in particular |
pthread_mutex_t |
Mutex<T>: the lock owns the data |
| Cancellation | context.Context |
flag + pthread_cancel (rarely) |
drop the future; CancellationToken |
| Data race | runtime race detector (-race) |
UB | compile error (Send/Sync) |
| Escape hatch | unsafe, cgo |
(the whole language) | unsafe { } blocks, extern "C" |
| Package manager | modules, go.mod |
none (system packages, vendoring) | Cargo, Cargo.toml + Cargo.lock |
| Formatter / linter | gofmt, go vet |
clang-format, clang-tidy |
rustfmt, clippy |
| Tests | go test, _test.go |
hand-rolled harness | cargo test, #[test], doc tests |
| Vulnerability scan | govulncheck |
— | cargo audit / cargo deny |
The one-sentence version of each language's memory model:
- C: every pointer is a promise you made, and the compiler believes it.
- Go: every pointer keeps its target alive, and the GC sorts it out.
- Rust: every value has one owner, and borrows cannot outlive it or conflict with it. The compiler checks both rules.