DEV Community

dami
dami

Posted on

Rust vs Go? Which should you choose in 2024

Introduction

Rust and Go are languages with applications in performance-critical applications. This article breaks down the main features and typical use cases for both languages.

Within the last decade, Rust and Go have grown quite popular. Memory-safe Rust is mainly used in systems programming. Go is favored because of its simplicity and built-in concurrency, which makes it perfect for building scalable web applications and APIs. Interestingly, the largest technology firms, such as FAANG and Fortune 100 companies, use both Rust and Go for different aspects of their applications.

In this article, you will discover the answer to the question "Should I Rust or Should I Go?". You'll learn how Rust and Go compare to each other in terms of concurrency, and memory safety among others. Also, you'll learn about the different scenarios best suited to each language.

By the end of this article, you will be well informed of key features and use cases of both languages, leading you to an informed decision in choosing the right one for your project.

Overview of Rust

Rust is a high-level programming language with a strong focus on memory safety created by Graydon Hoare, a former Mozilla employee as a personal project in 2006. Memory-safe languages like Rust have been recommended by the United States Department.

Rust Logo

Key Features

  • Memory Safety: Rust enforces memory safety at compile time without using garbage collection.
  • Performance Comparable to C/C++: Rust’s is as fast as C and C++.
  • Ownership System: Rust supports concurrent operations using its ownership and borrowing system.
  • Strong Type System and Pattern Matching: Rust's type system and pattern matching features enhance code safety.

Overview of Go

Go is an open-source programming language created at Google by Robert Griesemer, Rob Pike, and Ken Thompson in 2009. It's statically typed and similar to C++ in syntax. In an interview, Rob Pike said Go was created because of the difficulty associated with concurrency operations in C++ at the time.

Go Logo

Key Features

  • Simplicity: Go has a moderate learning curve which makes it easier to work with.
  • Fast Compilation Times: Go compiles quickly, allowing for rapid development and iteration.
  • Built-in Concurrency: Go’s built-in goroutines and channels allow for concurrency operations.
  • Strong Standard Library: Go’s standard library is very robust.

Comparison: Rust vs Go

Performance

In this section, you'll learn how Rust and Go compare in terms of speed and memory usage.

1. Benchmark comparisons

Benchmarks Game compared the runtime and memory usage of both Rust and Go. For all the algorithms tested, it was discovered that the most optimized Rust code has a faster execution time compared to the most optimized Go code.

For the regex-redux and binary trees algorithms, Rust by far outperforms Go as shown in the images below. Rust code uses less memory and executes in a shorter time compared to Go.

Rust vs Go: regex-redux

Rust vs Go: binary-trees

2. Memory Management and Efficiency

Both Rust and Go are memory-safe languages although they achieve this in different ways. Rust by design favours fast execution while Go favours fast compilation. Rust's ownership and borrowing system prevent many common causes of memory leaks at compile time, while Go relies on automatic garbage collection to free up unused memory at runtime. However, both languages can still experience memory leaks under certain circumstances.

Concurrency and Parallelism

In this section, you'll learn about the unique approaches of Rust and Go to concurrency and parallelism.

1. Rust's Approach

Rust supports concurrency through the use of the async/await paradigm and the use of threads and channels.

  • Async/Await Paradigm in Rust

Rust's async/await paradigm allows you to write asynchronous code that is easier to read and maintain. Runtimes built on Rust's Future trait like Tokio or async-std are often used with the async/await paradigm. Here's an example of using async/await:

use tokio::time::{sleep, Duration};

async fn execute_task() {
    println!("Task has begun.");
    sleep(Duration::from_secs(2)).await;
    println!("Task is done.");
}

#[tokio::main]
async fn main() {
    let task_handle = tokio::spawn(async {
        execute_task().await;
    });

    task_handle.await.unwrap();
    println!("Main function completed.");
}
Enter fullscreen mode Exit fullscreen mode

In the code above, the execute_task function simulates a task that takes some time to complete. The Rust Tokio runtime manages the main function's execution without blocking the thread, allowing other asynchronous tasks to proceed concurrently. The main function then waits for the task to finish before printing a completion message.

Here's the output:

Rust async/await output

  • Using Threads and Channels

Rust's standard library provides support for threads and message-passing concurrency with channels. Here's an example:

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (sender, receiver) = mpsc::channel();

    thread::spawn(move || {
        let messages = vec![
            String::from("greetings"),
            String::from("from"),
            String::from("the"),
            String::from("worker"),
        ];

        for message in messages {
            sender.send(message).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    for received_message in receiver {
        println!("Received: {}", received_message);
    }
}
Enter fullscreen mode Exit fullscreen mode

In the code above, a new thread which runs concurrently with the main thread is created using thread::spawn(). This thread sends a series of messages through a channel created using mpsc::channel(). As messages are sent from the spawned thread, they are received and printed by the main thread.

Here's the output:
Rust thread output

2. Go's Approach

Go achieves concurrency through the use of goroutines and channels. Goroutines are lightweight threads managed by the Go runtime which allows functions to run concurrently. A regular function can be made into a goroutine by adding the go keyword in front of it.

  • Concurrency with Goroutines
package main

import (
    "fmt"
    "time"
)

func displayDigits() {
    for i := 1; i <= 5; i++ {
        time.Sleep(1 * time.Second) // sleep to demonstrate concurrency
        fmt.Printf("Digit: %d\n", i)
    }
}

func displayCharacters() {
    for i := 'A'; i <= 'E'; i++ {
        time.Sleep(1 * time.Second)
        fmt.Printf("Character: %c\n", i)
    }
}

func main() {
    // Launch the goroutines
    go displayDigits()
    go displayCharacters()

    // Wait for the goroutines to complete
    time.Sleep(6 * time.Second)
    fmt.Println("Finished")
}
Enter fullscreen mode Exit fullscreen mode

In this code above, two goroutines are defined. The first goroutine prints digits from 1 to 5, while the second prints characters from A to E. The main function launches these goroutines and then waits for 6 seconds so the goroutines have enough time to run before printing "Finished".

Here's the output
Goroutins output

Goroutines can communicate with each other using channels. Here's an example:

package main

import "fmt"

func transmitMessages(ch chan string) {
    msgs := []string{"Greetings", "Simplicity", "Concurrency"}

    for _, message := range msgs {
        ch <- message
    }

    // Properly close the channel after sending all messages
    close(ch)
}

func main() {
    ch := make(chan string)

    // Launch the transmission of messages concurrently
    go transmitMessages(ch)

    for message := range ch {
        fmt.Println(message)
    }
}
Enter fullscreen mode Exit fullscreen mode

In the code above, the transmitMessages function, running as a separate goroutine, sends a series of messages through a channel. Then, the main function receives these messages and prints them.

Here's the output:
Go Channels code output

Learning Curve and Development Speed

Here, you'll learn about the learning curve of both languages and the development speed.

Rust has a much steeper learning curve compared to Go which has been hailed by developers worldwide for its simplicity and easy-to-understand syntax. Rust on the other hand takes a whole lot more time to understand as developers often struggle with important concepts like its memory safety rules, type conversions, and type checks.

The same can be said about development speed because Go is easier to understand, and developers can begin working with it faster as opposed to Rust which can take a bit of time because of the steep learning curve.

Safety and Reliability

In this section, you'll learn about the different measures both languages set to allow for safety and reliability.

1. Rust’s Ownership System

In Rust, when a value is assigned to a variable or moved to a function, the ownership is transferred, leading to the original variable being inaccessible. This is to prevent double-free errors and data races. Rust's ownership system ensures memory safety by managing the memory allocation and deallocation process.

fn main() {
    {
        let c2 = String::from("Ownership model");
        let c3 = c2;
        println!("{}", c3);
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example, we have a string c2. When we assign c2 to c3, Rust invalidates c2. If you try to print c2, you'll get a compile-time error as shown in the image below.

Rust complile error

2. Go’s error handling

Unlike in most modern programming languages, errors in Go are not exceptions. They're simply values that implement the error interface. This approach allows for a more readable and maintainable code. Below is the error interface used by Go.

type error interface {
    Error() string
}
Enter fullscreen mode Exit fullscreen mode

Ecosystem and Community

When comparing Rust and Go, it's important to consider their ecosystems, community sizes, and corporate support

1. Community size and activity

Both Rust and Go have active and vibrant communities. Although Go stands out with more GitHub stars and active users compared to Rust. Below is the GitHub Page and the number of Stack Overflow questions asked for both languages.

Rust
Below is the Rust Github page with 96k stars and Stack Overflow page with over 42k questions tagged [rust].

Rust GitHub Page
Rust GitHub Stars

Rust Stackoverflow questions asked
Rust Stack Overflow Questions

Go
Below is the Go Github page with 122k stars and Stack Overflow page with over 73k questions tagged [go].

Go GitHub page
Go GitHub Stars

Go Stackoverflow questions asked
Go Stack Overflow Questions

According to a 2024 survey by Stack Overflow, developers voted Rust as the most admired programming language for 8+ years in a row.

2024 Survey by Stack Overflow

2. Corporate support and adoption

Rust is backed by Mozilla, and now by the Rust Foundation. Tech companies like Dropbox, Cloudflare, and Meta are using Rust for performance-intensive services.

Go was created at Google, and it has substantial corporate support and adoption. Major companies like Google, Uber and Dropbox rely on Go for many of their backend services. Docker, a leading containerization technology was built mainly in Go.

3. Popular Frameworks and Libraries

Rust:

  • Actix: A powerful, fast web framework.
  • Rocket: A web framework focused on ease of use and safety.
  • Serde: A widely-used library for serialization and deserialization.
  • Tokio: A runtime for writing asynchronous applications with Rust.

Go:

  • Gin: A lightweight web framework that’s easy to use.
  • Beego: An open-source, high-performance web framework.
  • GORM: The most popular ORM for Go, making it easy to handle databases.
  • Cobra: A library for creating powerful CLI applications.

Here is a table summarizing the key differences between each language.

Aspect Rust Go
Memory Safety Enforced at compile time without the need for garbage collection. Relies on a garbage collector.
Performance Comparable to C/C++. Slightly lower than Rust but fast enough for many applications.
Concurrency Model Utilizes an ownership model with threads and async tasks. Built-in support with goroutines and channels.
Type System Strong with pattern matching and type inference. Statically typed with a simpler type system.
Compilation Times Slower due to complex optimizations and safety checks. Faster compilation.
Ease of Use Steeper learning curve due to advanced features. Easier to learn.
Standard Library Rich but less extensive, focusing more on performance-critical and systems programming features. Comprehensive, especially strong in networking, I/O, and web server support.
Community and Ecosystem Rapidly growing, especially among systems programmers interested in safety and performance. Large and mature, widely used in cloud infrastructure, networking, and DevOps tools.
Error Handling Based on Result and Option types. Uses the error interface, treating errors as values.

When to use Rust

Rust particularly excels in performance and memory-critical scenarios or scenarios where a large amount of data is being processed. You can use Rust in the following scenarios:

  • Systems programming: Rust because of its memory safety can be used to build system-level programs like operating systems.
  • High-Performance Computing: Rust is ideal for applications that require exceptional performance.
  • Large-scale distributed system: Rust memory safety and speed make it an excellent choice when building distributed systems.

When to use Go

Go can be used in a variety of scenarios. Its built-in concurrency makes it a great choice for applications handling multiple requests. Overall, Go is a good fit if you value code simplicity and readability over performance. You should use Go if you need:

  • Concurrent operations: Go allows for concurrent operations using its goroutines.
  • Fast development: Go has a straightforward syntax with a standard library which allows for speedy development.
  • Simplicity and readability: Go’s easy-to-understand syntax makes it ideal for large teams.

Conclusion

At the end of the day, both Rust and Go are great choices when it comes to building server-side applications. However, the correct choice will be based on the requirements of your application and what you want to achieve.

This article covered the key features, use cases and differences between the Rust and Go languages, equipping you with the knowledge to decide on the best one according to your project requirements.

Resources

Here are some resources for further reading.

Top comments (3)

Collapse
 
tchisom17 profile image
Tchisom17

This is awesome. I look for to reading more of your articles!

Collapse
 
sbalasa profile image
Santhosh Balasa

Neither, try Nim :)

Collapse
 
thatcoolguy profile image
dami

Great choice!