Why Rust and Why Choosing It for Backend Development Is the Best Decision You Can Make?

Why Rust and Why Choosing It for Backend Development Is the Best Decision You Can Make?

The combination of high performance, reliability, and energy efficiency makes Rust an excellent choice for developing backend applications

·

11 min read

I have always dreamt of writing something in a systems programming language but never achieved it because of C++ footguns, such as memory errors, hidden costs, error-prone abstractions, the lack of a universal build tool, and poor ergonomics in the language ecosystem. Although C++ now supports smart pointers and iterator abstractions, they are still prone to misuse. Rust addresses many of these issues, allowing developers to write high-performance code without encountering the same footguns as in C++. Additionally, Rust has Cargo and many other utilities that follow best practices, enhancing the software development experience.

Here's the forward by Nicholas Matsakis and Aaron Turon from the Rust Book, describing the language's significance,

It wasn’t always so clear, but the Rust programming language is fundamentally about empowerment: no matter what kind of code you are writing now, Rust empowers you to reach farther, to program with confidence in a wider variety of domains than you did before.

Rust is a modern, open-source safe systems programming language. It is considered modern because it supports features typically found in high-level programming languages, such as Algebraic data types(ADT) for error handling, Pattern matching superior to switch statements, Closures, and Functional Programming style for processing collections using Iterators. Additionally, Rust incorporates Haskell-like type classes to design APIs in a more secure way. Each feature of Rust will be explained in upcoming posts.

It is termed a safe system programming language, distinguishing it from languages like C/C++, which are system programming languages but lack inherent safety.

Industry Decisions towards Rust

Linus Torvalds, the creator of Linux, views Rust as a second language for kernel development.

This is somewhat intriguing, as Linux doesn't consider C++ a secondary language. In other words, since Linux open-sourced, no programming languages have been integrated into the Linux kernel, apart from C and assembly.

Microsoft Azure CTO Mark Russinovich, in a Twitter statement, suggests a shift from starting new projects in C/C++ to using Rust for scenarios requiring a non-GC language. The Microsoft Security Response Center (MSRC) asserts that Rust represents the best alternative to C and C++ currently available.

There is more to a programming language than just the language itself. Tools surrounding programming languages make it easy to experiment, adopt, and get started. Rust has a variety of tools at its disposal to enhance productivity.

  • Cargo: A universal official build tool and package manager for Rust programming languages, akin to Pip for Python or Npm for JavaScript.

  • Clippy: A command used to find non-idiomatic Rust code, similar to ESLinter for JavaScript.

  • Rustfmt: A command-line tool for standardizing coding style.

  • Rustup: A command-line tool for installing Rust, its components, Cargo, targets for cross-compilation, and more.

  • Rust Language Server (RLS): Employed in intelligent IDEs for syntax highlighting, code completion, and error identification. Don't forget that Rust compiler error messages are human-friendly, or at least far superior to what you may be accustomed to.

  • Rust Docs: Include standard library documentation, The Rust Programming Language book, and the Rust reference book locally. This content should be downloaded during the Rust installation process and accessible without an internet connection.

  • RustDoc: Generates documents embedded in Rust source code and renders them on Doc.rs.

Stability

Rust follows a six-week update cycle, incorporating new features without disrupting existing code. The older Rust versions can still compile and function, but they may not be able to utilize the new features. Upgrading does not pose any issues. For more information, you can refer to the official statement on stability without stagnation from the Rust blog. This stands in contrast to the potential challenges encountered when upgrading versions in other languages.

Rust Foundation

Rust Foundation is a foundation supported by major industry players such as Facebook, Microsoft, Google, Spotify, and others. This implies that Rust is a language of the future, and learning it can make you a valuable asset.

Why Rust for Back end Application

When it comes to developing a backend application, the most common choices are Node.js if the chosen language is JavaScript, Django or Flask if the chosen language is Python, Ruby on Rails if the chosen language is Ruby, or Spring Boot if the chosen language is Java. However, all these backend web frameworks are neither highly performant nor entirely reliable. Despite their shortcomings, they are still widely used due to their ease of prototyping, user-friendly nature, and the availability of supported platforms for deployment, along with the tools surrounding them.

Problems will occur later in the development cycle, such as memory errors and concurrency bugs. Scaling can be detrimental in dynamic programming languages, requiring constant monitoring.

Low Memory Usage

Running a Rust program doesn’t require any runtimes, unlike Python where a CPython runtime must exist to run a Python program. Instead, Rust generates the binary as C++. Rust binaries are small and don’t have dependencies, which means they are easy to distribute. Containers like Docker could occupy less space, resulting in lower costs compared to servers written in Java Spring, Python Django, or Ruby on Rails for the same task in Rust.

How using aws lambda with Rust saved 3x the cost, which illustrates how using Rust can result in a threefold cost reduction compared to equivalent implementations in Python and .NET.

Tilde rewrote existing Java HTTP endpoints in Rust, reducing memory usage from 5GB to 50MB.

High Performance

Due to its low-level nature as a system programming language, we can access hardware features like SIMD(Single Instruction Multiple Data) and maximize performance for intensive workloads such as image and graphics processing, video encoding, and decoding. This level of control is not available in high-level languages like Gced because they abstract the underlying hardware. In Gced languages, you either need to access the respective runtimes, typically written in C/C++, which many application developers are not familiar with, or you must wait for the runtime to support those features.

Amazon S3 mountpoints written in Rust deliver millisecond latency responses (source).

Reading why Discord is switching from Go to Rust will show you that even though Go is faster than Java or Python, they experienced significant latency spikes due to the non-deterministic nature of the garbage collector invocation. This issue is completely avoided by using Rust. They implemented the backend caching system in Rust and are satisfied with Rust's performance.

Reliable and Robust

It's not that Rust won't crash; rather, programs written in Rust tend to be more reliable compared to those in JavaScript, Java, or Python-based backend servers. Most common errors, such as ConcurrentModify Exception (Iterator invalidation - a memory error), Null pointer exception, or Race condition, never make it into production because code with these bugs doesn't even compile in the first place. This confidence reduces the need for frequent intervention in production systems. The mantra becomes, "If it compiles, it works" (Please don't misinterpret).

Rust doesn't have any hidden runtime exceptions, as errors are explicitly handled. Its type system even aids in avoiding logic errors, such as mistakenly using variable assignment in places where a boolean was expected. I once read an engineering post that explained the costs of engineering efforts to reduce runtime exceptions.

Maintenance of large-scale projects in Rust compared to others.

Rust's strong type system and restrictions facilitate making code changes to large-scale projects without fear, as the compiler guides the process. Rust is competitive in the space of whole program analysis minimizing errors caused by subtle changes. It's no wonder that TypeScript is more preferable than plain JavaScript for large-scale projects.

The

Energy Efficiency

Rust is energy-efficient, requiring less compute power and reducing carbon emissions.

While energy efficiency alone may not be a compelling reason to choose Rust, it is a distinctive feature that only a few programming languages worldwide possess. Visit this link to explore how AWS is building sustainability with Rust: AWS Sustainability with Rust. It's noteworthy that the AWS Well-Architected Framework has now incorporated a sixth pillar, emphasizing the sustainability aspect—specifically, focusing on the energy efficiency of the cloud.

It's important to note that just because Rust is energy efficiency, it doesn't mean it's suitable for all tasks. Some tasks may be better suited to other programming languages, even if they lack energy efficiency. Nevertheless, the fact that Rust combines energy efficiency with high performance, memory safety, and reliability makes it an added advantage.

Fun Fact: If we write in Rust, we are contributing to low carbon emissions, benefiting the Earth's environment! 🌍🌿

Top-Notch Support for WebAssembly, a technology that has the potential to create an interoperable software ecosystem and improve the performance of front-end applications

These sweet spots, a combination of high performance, low memory usage, energy efficiency, reliability, and safety, are not easily achievable with other languages. For example:

1) C/C++ offers high performance, low memory usage, and energy efficiency but lacks reliability and is prone to memory safety errors. Furthermore, it lacks ergonomic APIs, a strength found in Rust.

2) Python, Java, and JavaScript provide memory safety but often fall short in terms of reliability and high performance. For instance, Rust servers can handle tens of thousands of requests per second, while these languages might only manage a small number of requests within the same timeframe

Rust provides a stable ecosystem for building high-performance and reliable networking applications.Notable crates include ,

  1. Tokio - A high-performance asynchronous runtime for executing Rust asynchronous code. It serves as the backbone for most widely-used web frameworks and anything dependent on asynchronous operations.

  2. Sqlx - An asynchronous database providing compile-time query validation. When SQL queries are represented as strings, we lack a way to detect invalidity before attempting to run the query. This is the power of Rust, even in a database library; it helps reduce bugs before reaching production.

  3. Reqwest - A high-level HTTP client library for testing and making HTTP requests.

  4. Tower - A modular library for building networking applications. Axum heavily relies on this library.

  5. Rustls - An alternative and secure TLS library, equivalent to OpenSSL.

  6. Serde - An efficient Serialization and Deserialization library for converting to various formats, including JSON. Rust web frameworks utilize Serde for serializing and deserializing from and to Rust types.

  7. Handlebars template - This is a template engine written in Rust. Other template engines are also available, such as mini Jinja, Askama, tera, and liquid.

For backend development, Rust provides high-level abstractions and ergonomic APIs, making it competitive with other widely used frameworks in terms of usage. Some of the widely used web frameworks in Rust include Actix, Axum, Rocket, and Warp.

This is a sample server using the Axum web framework to print 'Hello, World!' when visiting the index page. It listens on port 8080 on localhost and achieves this functionality in just 7 lines of code.

use axum::{routing::get, Router};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() {
    let routes = Router::new().route("/", get(|| async { "hello Word" }));
    let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
    axum::serve(listener, routes).await.unwrap();
}

Creating the same hello world server but with the Actix web framework.

use actix_web::{get, App, HttpServer, Responder};
#[tokio::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(index))
        .bind("127.0.0.1:8080")?
        .run()
        .await
}
#[get("/")]
async fn index() -> impl Responder {
    format!("Hello World")
}

Even though Rust is a low-level system programming language commonly used for developing operating systems, device drivers, and embedded systems, it's noteworthy how easy and ergonomic it is to build an API server in Rust. This ease of development is comparable to other frameworks, but Rust stands out for its superior performance, reliability, and efficient memory usage

Why Not Rust

Despite Rust having great advantages, it wouldn't be fair to discuss only the positive aspects of Rust.

The common intuition is that if you learn one programming language, then it's easy to learn another programming language as well. However, this does not align with Rust. Depending on the experience you have, you will always encounter some unique and different concepts in Rust, and it takes time to get used to them

1) Steep Learning Curve: Rust's unique concepts may pose challenges, requiring time to adapt. But even if you are not working with Rust, writing Rust makes you a better programmer.

2) Unlike Python and JavaScript, Rust is static; no virtual machine or garbage collector is needed to provide memory safety. Instead, Rust uses ownership and borrowing to mitigate memory errors at compile time.

3) From Haskell or F#, it offers more control over mutability, supporting all possible concurrency patterns.

4) Compared to C/C++, Rust also doesn't use a garbage collector for memory management. But Rust's way of handling memory feels automatic, employing a malloc-free-like pattern for allocating and deallocating memory and maintaining the same performance as C++. Rust discourages the use of raw pointers, instead providing different safe abstractions.

5) Rust doesn't have object-oriented features such as inheritance. Instead, it prefers composition.

If you are interested in backend development and AWS in Rust, stay tuned for the upcoming blog post. It will explain AWS SDK design and demonstrate how to use any AWS SDKs in Rust.

In conclusion, Rust presents a compelling option for backend development, balancing performance, reliability, and energy efficiency. While challenges exist, the language's strengths make it a strong contender in various application domains.

References