Learning Rust as a Go Developer

I’ve been writing Go for years. Recently I started learning Rust, and it’s been an interesting shift.

The two languages solve similar problems - concurrency, performance, systems stuff - but they think about them differently. Go gives you guardrails and gets out of the way. Rust makes you prove your code is correct before it’ll compile.

Some things translate pretty well. Concurrency concepts, for one. Go has goroutines and channels; Rust has threads and async/await with strong compile-time guarantees. The mental model of “don’t share memory, pass messages” works in both places. Tooling feels similar too - cargo is opinionated and batteries-included like the Go toolchain.

Ownership and borrowing is the big adjustment. In Go, the garbage collector handles memory and you don’t think about it much. In Rust, you have to understand who owns what and how long references live. The compiler is strict - it won’t let you compile code that might have dangling pointers or data races.

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1 is moved, not copied
    // println!("{}", s1);  // won't compile - s1 is gone
    println!("{}", s2);
}

At first this felt like fighting the compiler. After a while it started feeling like the compiler catching my mistakes before they became bugs in production.

Error handling is different too. Go’s if err != nil is simple but repetitive. Rust’s Result<T, E> and the ? operator are more expressive - errors are part of your function’s type signature, not just something you check afterward.

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;  // returns early if error
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

The type system in general has more machinery - traits, generics, lifetimes, macros. More powerful than Go’s interfaces and structs, but you pay for it in complexity.

Lifetimes still trip me up sometimes. I understand the concept, but the syntax and error messages can be cryptic. Same with knowing when to reach for Box, Rc, Arc, RefCell - I look that up more often than I’d like.

Different tools for different jobs. Go is great for networked services, CLIs, stuff where you want to move fast and the GC overhead doesn’t matter. Rust makes sense when you need predictable performance, no runtime, or you’re working closer to the metal.