Mozilla created Rust in response to security vulnerabilities in programs developed in the memory-unsafe languages C/C+ with the goal to achieve a secure systems programming language that does not compromise on performance.

Unlike other memory-safe languages like Java, Go and C#, Rust does not rely on a managed runtime with a garbage collector to achieve its safety. Instead, Rust offers a new programming model that lets the compiler track ownership and lifetimes of variables to achieve memory safety at compile time.

tCell’s agents run inside app servers to collect analytics and block attacks. Supported platforms include Java, .Net, Python, Ruby, NodeJS and agents are primarily implemented in each platform’s own language. The Java agent has for nearly a year shipped with part of its functionality implemented as a native library developed in the programming language Rust. Other agents have recently started to use the library to share functionality.

Safety First

Agents run inside our customers’ servers and deal with potentially malicious traffic so reliability and security are our primary concerns. Fortunately, this is also true for Rust. Rust’s memory safety prevents segmentation faults and automatically frees both memory and other resources such as file handles and network connections when the program no longer uses them. Rust also provides a strong type system that encodes concurrency properties and disallows null values to prevent runtime errors such as type casts, data races, and null pointers.

Fast Performance and Low Memory Footprint

Only second to safety, our agents must not result in significant overhead to our customers’ web applications. Rust’s performance is similar to that of C/C++ and offers a significant speedup over code written in languages such as Python or Ruby. Rust’s lack of a managed runtime keeps memory footprint to a minimum and the language does not need to run its own threads.

Excellent Tooling and Integration with C

Most languages can interact with C libraries through FFI libraries (Foreign Function Interface, or JNI for Java). Rust makes it easy both to use C libraries and to expose C-compatible interfaces to other languages.

Rust comes with a package manager called cargo that manages Rust’s modules called crates. Rust’s gcc and cmake crates abstract away platform-specific C compiler configurations to help integrate C libraries into cross-platform Rust builds. The bindgen tool helps generate Rust bindings for C libraries.

Fun fact, our first use of Rust was to use its build tools to support cross-platform builds of a C library.

Challenges

The speed of our continued rollout of Rust depends on overcoming a number of challenges.

Deployment

It is of extreme importance to us that it is as simple to deploy our agents as any other module written in the target platform language. Package managers such as NPM, Ruby Gems, and Python PIP support downloading and building C code as part of their packages. Build tools for C are also often already installed on Linux servers. Deployment of Rust libraries requires either Rust build tools to be installed on the machines or convincing package managers to deploy the right pre-compiled binaries.

Tools such as Helix for Ruby and Neon for NodeJS help write language add-ons in Rust, but have yet to target deployment.

Learning Curve

Rust’s special programming model along with advanced type system can initially be frustrating to learn, especially as the safety-focused compiler goes to great lengths to prevent incorrect code from compiling. The upside is that experienced agent developers new to Rust can quickly try to start contributing with little risk of creating problems at runtime.

Newness

The newness of Rust’s ecosystem sometimes shows in the availability and maturity of libraries, but this is mitigated by Rust’s support for integrating mature C libraries.

An example of this was that the pure-Rust regex crate relies on thread-local caching for performance. This is inappropriate for some of our threading situations, so we looked for alternatives and it turned out others had already published a wrapper for a mature regex library in C. The wrapper used Rust’s gcc and cmake crates to transparently build the C library from a source inside our Rust builds on Windows, Linux, and Mac.

Conclusions

Sometimes you need to reach below the managed runtime to access specialized functionality or to share implementations. In a world concerned with security and reliability, Rust provides a safer drop-in replacement for C/C++. The main speed bump we see for our Rust deployment is how easily the deployment tools on different platforms can accommodate the language and how quickly agent developers can learn the language and develop integrations with the managed runtimes.

 

About the author

William is focused on JVM-based data engineering technologies and also the maintainer of the Java version of tCell’s agent. William has been using Rust since its 1.0 release two years ago and considers it a welcome return to code below the JVM after having left the segfaulting world of C for Java in -96.