Rust vs. Go in 2025: An In-Depth Comparison
In this comparison of Rust and Go, taken from a full talk by our CTO, Alexander Kirillov, we look at both languages' real-world capabilities, performance, and use cases.
The long-running discussion of Rust vs. Go is becoming increasingly pertinent as we approach 2025. Go, known for its ease of use and quick learning curve, has been the go-to language for high-performance backend services. At the other end of the spectrum is Rust, which is quickly becoming famous for its memory safety guarantees and zero-cost abstractions.
The Background of Our Comparative Analysis
At Evrone, we had hands-on experience comparing Rust and Go on a large scale for our client’s project. The goal was to create a service to process JSON data, validate it, enrich it, and store it in ClickHouse for long-term analytics. It consisted of moving and verifying JSONs and providing data integrity and the best performance under the highest loads. We had an R&D department with Python and Go superpowers but limited Rust knowledge to work on this service. The experiment's results shed light on Go and Rust's advantages and shortcomings in dealing with such requirements.
Initial Observations and Setup
The initial tools the client had already used were studied and developed in the first part, mainly using Python. Python is great for working with data analytics and ETL processes. It is very flexible and easy to use but wasn’t fast enough to handle our high-load use case. At first, Python could only service around 3,000 to 3,500 requests per second per CPU, which was not nearly enough for the system.
Inspired by a similar use case study from Kion, we explored ways to improve performance while maintaining Python’s flexibility. We discovered Graniant, a tool that combines Python with a Rust backend using Actix Web. This led us to test three implementations: pure Go with Fiber, Python with Graniant, and pure Rust using Actix Web.
Rust vs. Go: The Performance Battle
When it came to performance, the Go implementation proved reliable and efficient. Our team, familiar with Go’s idiomatic practices, set up HTTP routers and handled serialization and deserialization using the Gin framework. Go’s ease of use allowed for a straightforward development process, with clear, readable code and a relatively low cognitive load. However, the real test came when we compared Go's raw performance metrics with Rust's.
The results for Rust's Actix Web were impressive, almost 1.5 times faster than Go under the same conditions. Rust had its performance tuned in so that it was primarily faster because it used zero-cost abstractions and offered compile-time guarantees. Unlike Go, having the serialization and deserialization code based on reflection at runtime caused runtime overhead. Rust provided the library for serialization and deserialization and compiler-generated efficient code at compile time with no need for runtime reflection. It resulted in better CPU usage and higher throughput.
Go’s flame graphs gave us a deeper dive into the limits of Go’s garbage collection and runtime reflection. Garbage collection took approximately 10% of processing time, and JSON processing required significant runtime reflection, leading to spikes that slowed processing down. Rust did not have this problem; type safety and compile time checks meant you could be more predictable and efficient in your code.
The Complexity Trade-off
Despite Rust’s impressive performance, our team encountered challenges due to its complexity. While Go’s straightforward, readable code made it easy for developers to understand and maintain, Rust’s intricate features, such as procedural macros and generics, demanded more from our team. Procedural macros in Rust allow developers to generate code and implement custom behaviors at compile time, which is powerful but comes with a steep learning curve. The deeper you go into Rust’s ecosystem, the more complex it becomes, requiring a solid grasp of its strict type system and the inner workings of traits and macros.
Our team’s experience writing web services in Rust demonstrated that while the language is highly capable, it requires a higher level of expertise. Rust's feature set can be advantageous for developers already familiar with systems programming or those comfortable with C++ or advanced JavaScript. However, Go’s simplicity and readability provide a significant advantage for teams focused on quick time-to-market. The ease of onboarding new developers and the predictability of Go’s codebase make it more approachable and suitable for rapid development cycles.
Need expert guidance in choosing between Rust and Go for your next project? At Evrone, our team has extensive experience working with both languages and can help you determine which fits your specific needs best. Reach out to us today and discuss how we can boost your development process and achieve the best results for your business!
Practical Examples and Key Takeaways
One practical comparison during our analysis involved creating HTTP routers in both languages. In Go, setting up a router using Gin was straightforward, with easy-to-read handlers and simple parameter extraction. The equivalent was achieved in Rust using Axum, a library similar in functionality to Gin but with added benefits due to Rust’s strong typing. Axum allowed for direct type-safe handling of query and path parameters, preventing runtime errors and making the code more predictable.
Another critical aspect was error handling. Go’s approach involves explicit checks, which, while simple, can lead to repetitive boilerplate. Conversely, Rust uses monadic types like Option and Result, which enable cleaner error propagation through the ? operator. This allows functions to return early on errors without breaking the flow, enhancing readability and reducing clutter.
Advanced Features and the Road Ahead
While both Go and Rust have unique features, Rust’s advanced capabilities, such as procedural macros, were particularly noteworthy. These macros facilitate complex behaviors and help eliminate repetitive code by generating it during compilation. However, their use requires an in-depth understanding of AST manipulation and token streams, which is not beginner-friendly.
In contrast, Go’s strength lies in its minimalistic design. Its garbage collector and consistent performance make it suitable for applications where development speed and simplicity are prioritized over raw execution efficiency. Go’s upcoming features, such as workspace management for modules, indicate ongoing improvements to maintain its relevance in 2025.
Rust, however, has positioned itself as a formidable tool in scenarios where memory safety and performance are non-negotiable. Its growing ecosystem and improved ergonomics have made it possible to develop web services that were once considered the domain of more straightforward languages like Go.
Final Thoughts: Choosing the Right Language
The comparison of Rust vs Go in 2025 highlights that both languages have their places in modern software development. Rust is a compelling choice for projects where performance and type safety are critical, such as real-time data processing and low-latency applications. However, its complexity can hinder productivity for teams unprepared for its steep learning curve.
Go remains the language of choice for teams focused on rapid development, scalability, and ease of onboarding. Its clean syntax, predictable behavior, and robust ecosystem make it ideal for startups and fast-paced development environments.
At Evrone, we understand that every project has unique needs. Whether you need the simplicity and reliability of Go or the high performance and safety guarantees of Rust, our team has the expertise to guide you through the decision-making process and development journey. Contact us to discuss your project and learn how we can help you leverage the best technology for your goals!