01open 25 min

Mindset shift: compiler-enforced design over runtime discipline

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

by the end of this lesson you can

  • Uses Result and ? intentionally
  • Keeps the control flow explicit
  • Lets the function signature communicate what can fail

Overview

Go developers are used to keeping code simple and relying on clear conventions at runtime. Rust still values clarity, but it pushes more design decisions into the type system and uses compiler feedback to keep those decisions honest.

In Go, you often

accept a simple design that stays readable and trust code review, tests, and discipline to catch many mistakes.

In Rust, the common pattern is

to encode more guarantees directly in types, ownership rules, and function signatures so mistakes are rejected before the program runs.

why this difference matters

The first transition is cultural. Rust feels slower until you start using the compiler as part of the design process rather than a final syntax check.

Go

cfg, err := loadConfig()
if err != nil {
    return err
}
run(cfg)

Rust

let cfg = load_config()?;
run(cfg)?;

Deeper comparison

Go version

user, err := loadUser(id)
if err != nil {
    return err
}
if user.Admin {
    log.Println("admin")
}

Rust version

let user = load_user(id)?;
if user.admin {
    println!("admin");
}

Reflect

What becomes easier to trust when the compiler is allowed to reject more vague or underspecified designs?

what a strong answer notices

A strong answer mentions clearer invariants, fewer hidden assumptions, and more explicit failure and ownership boundaries.

Rewrite

Rewrite this Go sketch into a Rust-style flow that makes failure part of the function signature.

Rewrite this Go

cfg, err := loadConfig()
if err != nil {
    return err
}
if cfg.Enabled {
    startWorker(cfg)
}
return nil

what good looks like

  • Uses Result and ? intentionally
  • Keeps the control flow explicit
  • Lets the function signature communicate what can fail

Practice

Describe how you would redesign a small Go service helper so it feels natural in Rust.

success criteria

  • Names which assumptions should move into types
  • Explains what ownership or borrowing would clarify
  • Treats compiler feedback as part of the design loop

Common mistakes

  • Expecting Rust to feel productive only when it behaves like Go.
  • Trying to remove compiler constraints instead of learning from them.
  • Treating exactness as overengineering before seeing what bugs it prevents.

takeaways

  • The first transition is cultural. Rust feels slower until you start using the compiler as part of the design process rather than a final syntax check.
  • A strong answer mentions clearer invariants, fewer hidden assumptions, and more explicit failure and ownership boundaries.
  • Names which assumptions should move into types