Why We Chose Rust as Our Programming Language
Why Rust continues to prove itself as a bridge between low and high-level languages.
Join the DZone community and get the full member experience.
Join For FreeChoosing a programming language for a new product is an important strategic decision. It has long-term implications for hiring, culture and even the viability of a product.
The first thing to consider is whether the language is viable for the particular problem statement you are trying to solve. Important questions are:
- How suitable is the language for your particular use case?
- Will it perform?
- Will it run on the targeted platform(s)?
These should be the primary questions. But there are other things that might influence your decision such as:
- How will choosing a particular language influence your turnaround time from idea to reality?
- What are the cost benefits of using a particular language?
- How easy would it be to solve new problems that you might stumble upon along the way?
Keeping these questions in mind, this article will try to explain our reasoning behind choosing Rust for our new product.
Use Case
We were creating a device to consume data from various sensors and provide real-time analytics and intelligent assistance through web and mobile applications. We needed a language that was fast enough to allow minimum real-time latency and use limited resources of an SoC device.
Here is how we evaluated programming languages for our project.
Performance
Comparing the cross-language performance of real applications is tricky. We usually don’t have the same expertise in multiple languages and performance is influenced by the algorithms and data structures that the programmer chooses to use. As the benchmarks below show, it is generally believed that Rust performs on par with C++ and performs much better than other interpreter or JIT based languages such as Lua or Python.
Concurrency
As described in the use case above, we wanted to process data in real-time from multiple sensors. Our target platform, SoC devices, use ARM-based CPUs and generally have 4+ cores. We wanted to utilize all CPU cores —having multithreading support was important.
Lua does not have native multithreading support. Though there are 3rd party workarounds, the performance and reliability of those are questionable. Rust, on the other hand, has built-in support for multi-threading and its ownership and borrowing rules help us write very safe concurrent code.
Memory Safety
Dynamically typed languages give you significant flexibility. Type changes do not need manual propagation through your program. It also gives you more mental flexibility, as you can think more in terms of transformations, operations, and algorithms. Flexibility lets you move faster, change things quickly, and iterate at a faster velocity. This it comes at a cost. It’s a lot easier to miss potential problems, and these problems are generally very hard to debug. Plus, these features generally come with a performance penalty.
On the other hand, in a static-typed language, a large number of errors are caught early in the development process. Static typing usually results in compiled code that executes faster because when the compiler knows the exact data types that are in use, it can produce optimized machine code. Static types also serve as documentation.
Rust goes above and beyond these points. Rust’s very strict and pedantic compiler checks each and every variable you use and every memory address you reference. It avoids possible data race conditions and informs you of undefined behavior.
The right part of the chart below shows concurrency and memory safety issues. These are the most complex and unpredictable classes of errors and are fundamentally impossible to get in the safe subset of Rust. Moreover, all these type-related bugs are dangerous and result in a variety of security vulnerabilities.
Type safety is one of the Rust’s biggest selling point and is the reason Rust was the most loved language for 3 consecutive years in StackOverflow Surveys.
Rust did this by using the concept of ownership of a variable. In Rust, every value has an “owning scope,” and passing or returning a value means transferring ownership to a new scope. You lend out the access to the functions you call — a precoss called “borrowing.” Rust ensures that these leases do not outlive the object being borrowed. This not only makes it very type safe, but, also helps you tackle concurrency head-on because memory safety and concurrency bugs often come down to code accessing data when it shouldn’t.
Developer Experience
Rust has a steep learning curve. Most of this is due to ownership and borrowing concepts we discussed above. Rust requires you to be very aware of basic computing principles regarding memory allocation and concurrency, and it requires you to keep these principles in mind while implementing. This should be the case for any language, but particularly in Rust, you are explicitly forced by the compiler to write optimum memory-safe code.
Despite the fact that you’re doing things like manual memory management that you do in C++, Rust has many features and conveniences that almost make it feel like a high-level language.
Low-level control and high-level safety promises developers far more control over performance without having to take on the burden of learning C/C++.
Rust also facilitates writing tests within the program itself through its powerful libraries like cargo test. Combined with Bitbucket Pipelines, it becomes very easy to set up continuous integration and ship code with confidence.
Conclusion
Rust was a good choice for our project. When you want to implement a high-performance concurrent system with low resource footprint, the choice of programming languages is limited. Interpreter-based languages tend to perform poorly in high concurrency and low resource environments. System programming languages are the ideal candidates for such use cases.
However, interpreter-based languages may be better when you don’t have resource constraints and when high concurrency is not needed or is achieved using other mechanisms such as event loops.
C/C++ is the holy grail of systems programming languages. But there’s a reason C /C++ are also some of the most dreaded languages in StackOverflow Surveys. For newer programmers coming out of other high-level languages, approaching C/C++ is hard. The learning curve is very steep. There are approximately 74 different build systems and a patchwork framework of 28 different package managers, most of which only support a certain platform/environment and are useless outside of it. After 30+ years of evolution, new programmers have too much thrown at them.
Rust, on the other hand, is comparatively easier to approach, has a sizable community, does not come with decades of technical debt, and yet provides comparative performance. Memory safety and easier concurrency are just added benefits.
Published at DZone with permission of Mohit Agrawal. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments