Origins and philosophy

Go, created by Google in 2009 by Robert Griesemer, Rob Pike, and Ken Thompson, was born with a precise goal: simplicity. Its creators wanted a language that was easy to learn, fast to compile, and ideal for developing distributed services and concurrent systems.

Rust, initially developed by Mozilla Research in 2010, has a different mission: to offer performance similar to C/C++ while ensuring memory safety without a garbage collector. Rust's motto could be "zero-cost abstractions" with "memory safety".

Memory management: two philosophies compared

Here we find the most fundamental difference between the two languages.

Go uses an automatic garbage collector. This means developers don't have to worry about manually allocating and deallocating memory. Go's GC has been optimized over the years to reduce pauses (latency), making it suitable even for low-latency applications. The downside? Performance overhead and limited control over memory management.

Rust instead adopts the ownership system, a unique system that guarantees memory safety at compile-time without runtime overhead. Each value has an "owner", and when the owner goes out of scope, memory is automatically freed. The borrow checker verifies that there are no data races or invalid pointers at compile time.

// Go - automatic management with GC
func process() {
    data := make([]byte, 1024)
    // use data...
    // GC will handle cleanup
}
// Rust - explicit ownership
fn process() {
    let data = vec![0u8; 1024];
    // use data...
    // memory freed automatically here
}

Performance and control

Both languages are compiled to native code and offer excellent performance, but with different nuances.

Go is fast and predictable. Compilation times are exceptionally quick, fundamental for iterative development cycles. Runtime performance is excellent for most web applications and microservices, although the garbage collector introduces minimal overhead.

Rust offers performance comparable to C/C++, often superior to Go in CPU-intensive scenarios. The absence of a garbage collector and granular memory control allow for extreme optimizations. The downside? Compilation times are significantly longer.

Learning curve

Go was designed to be learned in a few days. The syntax is minimal, there are few keywords, and conventions are clear. A developer with experience in languages like Python or PHP can be productive in Go very quickly. Personally, I found the transition very natural.

Rust has a notoriously steep learning curve. Concepts like ownership, borrowing, lifetimes, and the borrow checker can frustrate even experienced developers in the initial phases. However, once these concepts are understood, they offer unparalleled control and safety.

Concurrency: goroutines vs async/await

Go excels in concurrency management. Goroutines are lightweight threads managed by the runtime, incredibly cheap to create (you can have thousands or millions). Channels offer an elegant mechanism for communication between goroutines.

func main() {
    ch := make(chan string)
    
    go func() {
        ch <- "message"
    }()
    
    msg := <-ch
    fmt.Println(msg)
}

Rust offers different concurrency models. The ownership system prevents data races at compile-time. It supports both traditional threading and async/await, but requires choosing a runtime (like Tokio) and a deeper understanding of concepts.

use tokio;

#[tokio::main]
async fn main() {
    let handle = tokio::spawn(async {
        "message"
    });
    
    let msg = handle.await.unwrap();
    println!("{}", msg);
}

Ecosystem and tooling

Go offers a "batteries included" experience:

  • - go mod for dependency management
  • - gofmt for automatic code formatting
  • - go test for integrated testing
  • - Integrated tooling for profiling and debugging
  • - Fast compilation

Rust has Cargo, probably the best package manager in the ecosystem:

  • - Excellent dependency management with crates.io
  • - Integrated build system
  • - Testing framework
  • - Documentation generation
  • - rustfmt and clippy for code quality
  • - Compilation is slower but produces optimized binaries

Ideal use cases

When to choose Go:

  • - Microservices and REST APIs
  • - Web backend applications
  • - DevOps tools and CLI
  • - Distributed systems
  • - Cloud-native services
  • - When immediate productivity is needed
  • - Teams with diverse skill sets

When to choose Rust:

  • - Embedded systems
  • - Game engines and graphics
  • - Performance-critical applications
  • - Operating systems and drivers
  • - WebAssembly
  • - Cryptography and security
  • - When every millisecond counts
  • - When memory safety is critical

Error handling

Go uses the explicit pattern of returning errors:

func readFile(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    return data, nil
}

This approach is verbose but clear. Every error must be explicitly handled.

Rust uses the type system with Result<T, E> and Option<T>:

fn read_file(path: &str) -> Result<Vec<u8>, std::io::Error> {
    std::fs::read(path)
}

// With the ? operator
fn process() -> Result<(), Error> {
    let data = read_file("file.txt")?;
    Ok(())
}

Rust's approach is more functional and type-safe.

Community and adoption

Go is widely adopted, especially in cloud and DevOps. Docker, Kubernetes, and Terraform are all written in Go. The community is mature and there are abundant learning resources.

Rust has a passionate and growing community. It has been voted as the "most loved language" on Stack Overflow for several consecutive years. Adoption is growing rapidly, especially in areas where C/C++ were dominant.

The "rewritten in Rust" phenomenon: a necessary reflection

In recent years we've witnessed a real race toward "rewritten in Rust". Traditional utilities, CLI tools, and even entire operating system components are being rewritten in Rust, often with great media emphasis. But we need to make an important reflection:

Software doesn't become safer just because it's written in a language that promises memory safety.

Rust guarantees memory safety, and that's an extraordinary achievement. But software security is a much broader concept that includes:

What Rust does NOT solve automatically

Logic bugs: An algorithm implemented incorrectly remains incorrect, regardless of the language. If your business logic has a flaw, Rust won't flag it.

Application-level security vulnerabilities: SQL injection, XSS, CSRF, authentication bypass, privilege escalation. These vulnerabilities exist at the application design level, not memory management.

Design flaws: Poorly designed architecture produces fragile software in any language. Badly designed APIs, anti-patterns, wrong architectural choices have nothing to do with the language.

Unsafe dependencies: Your code can be memory-safe, but if you use a crate with vulnerabilities, the problem remains. The dependency ecosystem is vast, and supply chain security is a critical issue.

Configuration errors: Many vulnerabilities arise from incorrect configurations, poorly set permissions, exposed secrets. Rust can't do anything about this.

Human errors: Hardcoded passwords, API keys committed to public repositories, deployment errors - these are human problems, not technical ones.

The cost of rewrites

Rewriting existing and stable software in Rust has significant costs:

Time and resources: A complete rewrite requires months or years of work. That time could be invested in new features or improving the existing codebase.

Bug regression: Every rewrite introduces the risk of reintroducing already-solved bugs or creating new ones. Legacy software, while not perfect, has accumulated years of bug fixes and handled corner cases.

Loss of tribal knowledge: Existing code often contains solutions to non-obvious problems, workarounds for edge cases documented only in the code itself.

Ecosystem maturity: A tool written in C or Go for 10 years has a mature ecosystem, extensive documentation, established community. The Rust version must rebuild all of this.

When rewriting in Rust makes sense

I don't want to demonize Rust rewrites, in some cases they make perfect sense:

  • - When there are evident memory safety problems in existing code causing recurring CVEs
  • - For critical components where performance and security are absolutely priorities (e.g., parsers, codecs, cryptography libraries)
  • - When software is in active maintenance and a major refactoring is already being planned
  • - For new projects where there's no legacy to maintain
  • - When the team has Rust competencies and can maintain the code long-term

The rewrite craze

The developer community loves to rewrite. It's in our nature: to see legacy code and think "I would do it better". But this bias makes us systematically underestimate:

  • - The complexity of existing software
  • - Use cases we don't know about
  • - Historical reasons for certain choices
  • - The real time necessary

The "rewritten in Rust" phenomenon is often driven more by enthusiasm for technology than by real needs. It's like the old craze to rewrite everything in Node.js, or before that in Ruby, or before that in Java.

A pragmatic approach

Before rewriting something in Rust, ask yourself:

  1. What's the real problem I'm solving? Is it really a memory safety problem or another type of problem?
  2. Are there less drastic alternatives? Can I improve the existing code? Can I use better tooling, static analysis, fuzzing?
  3. Do I have the necessary resources? Team, time, skills to complete and maintain a full rewrite?
  4. Is the current software really problematic? Or does it work well and I just want to use new technology?
  5. What would happen if I invested this time in something else? New features? Better documentation? Developer experience?

Rust is a tool, not a religion

Rust is an excellent language with unique characteristics. But it remains a tool. Like Go, like Python, like PHP, like any other language, it has a domain where it excels and cases where it's overkill.

Memory safety is important, but it's not the only aspect of software quality. A well-written, tested, reviewed Go program with good security practices is infinitely better than a poorly written Rust program with logical vulnerabilities and design flaws.

Software quality derives from the quality of developers, processes, and design care - not from the programming language.

Instead of chasing the next technology hype, we should focus on:

  • - Writing readable and maintainable code
  • - Implementing comprehensive tests
  • - Doing serious code reviews
  • - Documenting well
  • - Using static analysis and security scanning
  • - Following security best practices
  • - Truly understanding our application's security problems

"Rewritten in Rust" is fine when motivated by real needs. But when it becomes a cargo cult or an excuse to chase technology hype, we're simply wasting resources that could be used to actually improve the security and quality of our software.

There is no absolute winner. The choice between Go and Rust depends on context:

Choose Go if:

  • - You want to be productive quickly
  • - You're building web services or microservices
  • - The team has heterogeneous skills
  • - Simplicity and maintainability are priorities
  • - The garbage collector isn't a problem

Choose Rust if:

  • - You need maximum performance
  • - Memory safety is critical
  • - You work on embedded or low-level systems
  • - You can invest time in learning
  • - Total control over memory is necessary

Personally, in my experience with Go, I greatly appreciated the productivity and simplicity for web projects and microservices. Rust certainly represents the next step for those who want to push toward domains where control and performance are fundamental.

The good news? Both languages will continue to evolve and coexist, each excelling in its own domain. And the choice of language, however important, remains secondary to the quality of design, processes, and the people developing the software.

You can to read this content in Italian, and follow me on Medium or you can read this content on LinkedIn and discuss it with me