A programming language can be fast, safe, or easy to write. As developers, we get to choose our priorities but we can only pick two. Programming languages that emphasize convenience and safety tend to be slow (like Python). Languages that emphasize performance tend to be difficult to use and quick to blow things up (like C and C++). That has been the state of software development for a good long time now.

Is it possible to deliver speed, safety, and ease of use in a single language? The Rust language, originally created by Graydon Hoare and currently sponsored by Google, Microsoft, Mozilla, Arm, and others, attempts to bring together these three attributes in one language. (Google’s Go language has similar ambitions, but Rust aims to make fewer concessions along the way.)

Rust is meant to be fast, safe, and reasonably easy to use. It’s also intended to be used widely, and not simply end up as a curiosity or an also-ran in the programming language sweepstakes. Good reasons abound for creating a language where safety sits on equal footing with speed and development power. After all, there’s a tremendous amount of software—some of it driving critical infrastructure—built with languages that did not put safety first.

Rust language advantages

Rust started as a Mozilla research project partly meant to reimplement key components of the Firefox browser. The project’s priorities were driven by the need to make better use of multicore processors in Firefox, and the sheer ubiquity of web browsers meant that they must be safe to use.

But it turns out all software needs to be fast and safe, not just browsers. So, Rust evolved from its origins as a browser component project into a full-blown language project.

This article is a quick look at the key characteristics that make Rust an increasingly popular language for developers seeking an alternative to the status quo. We’ll also consider some of the downsides to adopting Rust.

Rust is fast

Rust code compiles to native machine code across multiple platforms. Binaries are self-contained, with no external runtime apart from what the operating system might provide, and the generated code is meant to perform as well as comparable code written in C or C++.

Rust is memory-safe

Rust won’t compile programs that attempt unsafe memory usage.

In other languages, many classes of memory errors are discovered when a program is running. Rust’s syntax and language metaphors ensure that common memory-related problems in other languages—null or dangling pointers, data races, and so on—never make it into production. The Rust compiler flags those issues and forces them to be fixed before the program ever runs.

Rust features low-overhead memory management

Rust controls memory management via strict rules. Rust’s memory-management system is expressed in the language’s syntax through a metaphor called ownership. Any given value in the language can be “owned,” or held and manipulated, only by a single variable at a time. Every bit of memory in a Rust program is tracked and released automatically through the ownership metaphor.

The way ownership is transferred between objects is strictly governed by the compiler, so there are no surprises at runtime in the form of memory-allocation errors. The ownership approach also means that Rust does not require garbage-collected memory management, as in languages like Go or C#. (That also gives Rust another performance boost.)

Rust’s safety model is flexible

Rust lets you live dangerously, up to a point. Rust’s safeties can be partly suspended where you need to manipulate memory directly, such as dereferencing a raw pointer à la C/C++. The key word here is partly, because Rust’s memory safety operations can never be completely disabled. Even then, you almost never have to take off the seatbelts for common use cases, so the end result is software that’s safer by default.

Rust is cross-platform

Rust works on all three major platforms: Linux, Windows, and macOS. Others are supported beyond those three. If you want to cross-compile, or produce binaries for a different architecture or platform than the one you’re currently running, some additional work is involved. However, one of Rust’s general missions is to minimize the amount of heavy lifting needed for such work. Also, although Rust works on the majority of current platforms, its creators are not trying to have Rust compile everywhere—just on whatever platforms are popular, and wherever they don’t have to make unnecessary compromises to the language to do so.

Rust is easy to deploy

None of Rust’s safety and integrity features add up to much if they aren’t used. That’s why Rust’s developers and community have tried to make the language as useful and welcoming as possible to both newcomers and experienced developers.

Everything needed to produce Rust binaries comes in the same package. You only need external compilers like GCC if you are compiling other components outside the Rust ecosystem (such as a C library that you’re compiling from source). Windows users are not second-class citizens here, either; the Rust toolchain is as capable on Windows as it is on Linux and macOS.

Rust has powerful language features

Few developers want to start work in a new language if they find it has fewer or weaker features than the ones they’re already using. Rust’s native language features compare favorably to what languages like C++ have: Macros, generics, pattern matching, and composition (via “traits”) are all first-class citizens in Rust. Some features found in other languages, like inline asembler, are also available, albeit under Rust’s “unsafe” label.

Rust has a useful standard library

A part of Rust’s larger mission is to encourage C and C++ developers to use Rust instead of those languages whenever possible. But C and C++ users expect to have a decent standard library—they want to be able to use containers, collections, and iterators, perform string manipulations, manage processes and threading, perform network and file I/O, and so on. Rust does all that, and more, in its standard library. Because Rust is designed to be cross-platform, its standard library can contain only things that can be reliably ported across platforms. Platform-specific functions like Linux’s epoll have to be supported via functions in third-party libraries such as libc, mio, or tokio.

It is also possible to use Rust without its standard library. One common reason to do so is to build binaries that have no platform dependencies — e.g., an embedded system or an OS kernel.

Rust has many third-party libraries, or ‘crates’

A measure of a language’s utility is how much can be done with it thanks to third parties. Cargo, the official repository for Rust libraries (called “crates”) lists some 60,000-plus crates. A healthy number of them are API bindings to common libraries or frameworks, so Rust can be used as a viable language option with those frameworks. However, the Rust community does not yet supply detailed curation or ranking of crates based on their overall quality and utility, so you can’t tell what works well without trying things yourself or polling the community.

Rust has strong IDE support

Again, few developers want to embrace a language with little or no support in the IDE of their choice. That’s why the Rust team created the Rust Language Server, which provides live feedback from the Rust compiler to IDEs such as Microsoft Visual Studio Code.

rust visual studio code

Live feedback in Visual Studio Code from the Rust Language Server. The Rust Language Server offers more than basic syntax checking; it also determines things like variable use.

Downsides of programming with Rust

Along with all of its attractive, powerful, and useful capabilities, Rust has its downsides. Some of these hurdles trip up new “rustaceans” (as Rust fans call each other) and old hands alike.

Rust is a young language

Rust is still a young language, having delivered its 1.0 version only in 2015. So, while much of the core language’s syntax and functionality has been hammered down, a great many other things around it are still fluid. Asynchronous operations, for example, are still a work in progress in Rust. Some parts of async are more mature than others, and many parts are provided via third-party components.

Rust is difficult to learn

If any one thing about Rust is most problematic, it’s how difficult it can be to grok Rust’s metaphors. Ownership, borrowing, and Rust’s other memory management conceits trip everyone up the first time. A common rite of passage for newbie Rust programmers is fighting the borrow checker, where they discover firsthand how meticulous the compiler is about keeping mutable and immutable things separate.

Rust is complex

Some of the difficulty of learning Rust comes from how its metaphors make for more verbose code, especially compared to other languages. For example, string concatenation in Rust isn’t always as straightforward as string1+string2. One object might be mutable and the other immutable. Rust is inclined to insist that the programmer spell out how to handle such things, rather than let the compiler guess.

Another example is how Rust and C/C++ work together. Much of the time, Rust is used to plug into existing libraries written in C or C++; few projects in C and C++ are rewritten from scratch in Rust. (And when they are, they tend to be rewritten incrementally.)

Rust is a systems language

Like C and C++, Rust can be used to write systems-level software, since it allows direct manipulation of memory. But for some jobs, that’s overkill. If you have a task that is mainly I/O-bound, or doesn’t need machine-level speed, Rust might be an ungainly choice. A Python script that takes five minutes to write and one second to execute is a better choice for the developer than a Rust program that takes half an hour to write and a hundredth of a second to run.

The future of Rust

The Rust team is conscious of many of its issues and is working to improve them. For example, to make it easier for Rust to work with C and C++, the Rust team is investigating whether to expand projects like bindgen, which automatically generates Rust bindings to C code. The team also has plans to make borrowing and lifetimes more flexible and easier to understand.

Still, Rust succeeds in its goal to provide a safe, concurrent, and practical systems language, in ways other languages don’t, and to do it in ways that complement how developers already work.