tracks / go-to-rust
Go
Rust

A guided path for Go developers who want to think in Rust.

Add stronger guarantees to a familiar backend mental model.

This track is built for experienced Go developers who want Rust's stronger guarantees without beginner-level detours. It focuses on compiler-guided design, explicit state modeling, Result and Option, Rust's testing style, and concurrency patterns that feel different from ordinary Go habits.

6 lessons~2.5 hrscomparison-firststatus: stableupdated Apr 2026

before you start

  • $You already write real Go code. This path assumes professional instincts, not beginner-level programming lessons.
  • $Expect comparison-first modules: each stop starts from familiar Go habits, then shows where Rust agrees or pushes back.
  • $Plan to practice the target ecosystem directly — syntax, testing, and design choices should feel native by the end, not translated.

by the end you can

  • Read and write idiomatic Rust without translating every line back through Go.
  • Spot which Go instincts transfer cleanly and which ones need a different Rust mental model.
  • Use the six modules as a working checklist when you build your first real Rust tool, service, or feature.

syllabus

6 modules, one capstone.

6 lessons · ~2.5 hrs
module 01
1 lesson · ~25 min

Mindset shift

Move from Go's simplicity-and-discipline culture to Rust's compiler-guided design loop.

module 02
1 lesson · ~25 min

Syntax and structure

Map familiar Go file and type organization onto Rust modules, structs, and impl blocks.

module 03
1 lesson · ~25 min

Data and state modeling

Use enums and pattern matching to model explicit state machines more directly than typical Go code.

module 04
1 lesson · ~25 min

Error handling

Translate from error returns and zero-value habits into Result, Option, and ?.

module 05
1 lesson · ~25 min

Testing in Rust

Map Go's standard-library testing habits onto Rust's #[test], assertions, and nearby test modules.

module 06
1 lesson · ~25 min

Concurrency and shared state

Learn how Rust handles concurrent work and shared state for developers coming from goroutines and channels.

track reference

Keep the shared translation aids in one place.

These are track-wide lookup tables and task patterns, not lesson-specific reading. Use them when you need a quick reset on the recurring source-to-target language translations.

Data type mappings

Recheck the building blocks when a translation starts to wobble.

Go

string

Rust

String or &str

Common operations

  • Go strings stay simple; Rust adds owned versus borrowed string choices.
  • Formatting moves from fmt.Sprintf to format!.
  • Borrowing decisions become part of normal string work in Rust.

Go

slice / array-like collection

Rust

Vec<T>

Common operations

  • Append with append(...) in Go and push(...) in Rust.
  • Rust iterator chains replace many loop-based transformations.
  • Element ownership and borrowing affect how collection code is shaped.

Go

map / keyed data

Rust

HashMap or struct

Common operations

  • Use HashMap when keyed data is truly dynamic and structs when the shape is known.
  • Keep legal fields and access patterns explicit in Rust instead of staying too map-shaped.
  • Choose the more precise data model earlier than you might in Go.

Go

error / nil

Rust

Result / Option

Common operations

  • Use Result for failure and Option for ordinary absence.
  • Keep absence and failure separate instead of mixing them in one contract.
  • Let the caller see success and failure explicitly in the return type.

Go

struct state

Rust

enum

Common operations

  • Use enums when legal states matter instead of structs plus booleans.
  • Let match stay exhaustive so transitions are explicit.
  • Reach for structs when data shape is stable and for enums when variant state is the core idea.

Comparative cheat sheet

Keep the most common tasks visible while you practice.

TaskGoRust
Define a functionfunc greet(name string) string { return "hello " + name }fn greet(name: &str) -> String { format!("hello {}", name) }
Format a stringmsg := fmt.Sprintf("user=%d", id)let msg = format!("user={}", id);
Add to a collectionitems = append(items, value)items.push(value);
Handle failing dataif err != nil { return err }if result.is_err() { return result; }
Test a functionfunc TestSlugify(t *testing.T) { ... }#[test] fn slugify_works() { assert_eq!(slugify("Hello"), "hello"); }

capstone

Ship a small Rust project that proves the mental model stuck.

Build one focused artifact in Rust using the same comparison-first habits from the track: start from a familiar Go shape, then deliberately redesign it the way Rust expects.

  • Translate a familiar Go data flow into idiomatic Rust structure instead of preserving the old shape by force.
  • Apply the early modules to data modeling, control flow, and API boundaries before you add tooling, polish, or deployment concerns.
  • Use the later modules to verify, test, and package the result the way Rust developers expect to see it shipped.

ready?

Start with module 01 — Mindset shift.

Begin