Abstract:
Putting a Roof over your Head: Object-Oriented Programming in Rust
Dr. Thomas Wahl, GrammaTech, Inc.
The Rust programming language addresses memory safety by restricting the programmer’s memory-access interface, in a way that provably rules out entire classes of errors, such as use-after-free. Not requiring a garbage collector, the language permits the design of highly efficient code, comparable to C++. The combination of memory-safe programming and high performance potential has made Rust a primary contender for the language of choice in systems programming.
On the other hand, Rust lacks support for certain classic object-oriented (OO) programming features: structs cannot be defined to inherit data members from other structs, and exceptions raised in code are not propagated up the call stack to find appropriate handlers. The absence of these popular features of mainstream OO systems languages exacerbates the already steep learning curve of Rust for many programmers with a Java or C++ background. This curve is often quoted as the main impediment to a more widespread adoption of Rust.
In this work, we present a Rust language extension called Roof: “Rust with Object-Oriented Features”, which allows programmers to express the OO features of inheritance and exceptions. Our main contribution is a program transformation to transpile these expressions into genuine Rust code, which can then be compiled using off-the-shelf tools. The Roof-to-Rust transpilation outputs idiomatic, human-readable Rust code. As a second benefit, Roof can therefore be used by programmers new to Rust to study the relationship between classic OO features and modern, Rust-specific idioms like traits and enum variants for error handling. Finally, Roof allows the migration of legacy code in languages that make heavy use of inheritance and exceptions (notably C++) into very similarly behaving (“bisimilar”) Rust code. Behavioral compatibility facilitates reasoning about the correctness of automated code migration. We are exploiting this benefit in GrammaTech’s ongoing work on the C++-to-Rust migration tool CRAM.
We illustrate the transpilation approach for the case of exception handling. Our goal is a full-stack try/throw/catch-like mechanism for Rust, resembling that found in C++. Roof adds these keywords to Rust’s syntax. To transpile a Roof program into Rust, every function that may—directly or indirectly—throw an exception is refactored to return an enum (union) that is either an expected result or an error. When such a function returns an error and the function call is inside a try block, we compare the exception type received to those handled by the catch clauses corresponding to the try. If we find a sub-type relationship, we refactor the code to execute that catch clause. If no sub-type match is found, or there is no try block, the error is simply returned to the previous caller. A risk to scalability is the possibility that a large fraction of functions may throw and require refactoring. We address this by computing the fan-out of all try blocks of the program, i.e., the functions reachable from a try block. Only functions in that fan-out need to be considered for refactoring.
In summary, Roof is a dual-use capability (legacy code migration; Rust enhancement) that equips a language focused on memory safety with widely embraced OO features. The ultimate goal is to accelerate Rust adoption by flattening the learning curve, without compromising safety and other benefits.
This material is based upon work supported by the Defense Advanced Research Projects Agency (DARPA) under Contract No. HR0011-22-C-The views, opinions, and/or findings expressed in this document are those of the author and should not be interpreted as representing the official views or policies of the Department of Defense or the U.S. Government. DISTRIBUTION STATEMENT ‘A’ (Approved for Public Release. Distribution Unlimited)