Why I Migrated from C++ to Rust (Development Experience in the Age of

Summary

  • C++ lacks a well integrated package system, making dependency management and reuse of results difficult.
  • Rust, centered around cargo, makes it easy to split results into crates and share them, enabling collaborative ecosystem growth.
  • For use cases where libraries are called from Python, Julia, or Fortran, Rust integrates cleanly via a C API while keeping the backend memory safe.
  • When development assumes assistance from generative AI, Rust’s learning curve and the cost of refactoring design changes can be significantly reduced.
  • Rust is well-suited for robust core implementations, while Julia excels at rapid experimentation; combining the two via C FFI works particularly well.

Please refer to an article by Terasaki-san on technical details.

Introduction

This article is a follow-up to my previous post, "What Was Painful About Building a Numerical Library in C++" (only in Japanese!).
We migrated the library libsparseir, initially developed in C++, to Rust.
Based on that experience, I explain why I decided to move from C++ to Rust.

Background

libsparseir is a C++ reimplementation of predecessor libraries written in Python and Julia.
Because C++ does not provide ABI compatibility, using such libraries from other languages is inherently difficult.
To address this, only a C FFI was exposed, enabling usage from other languages.
The goal was to unify the backend of sparse ir and SparseIR.jl and thereby reduce maintenance costs.

However, the lack of a standard package manager, compiler, and build system made development increasingly painful.

Around that time, I had an opportunity to work with Rust in another project.
As an exercise, I implemented a Rust version of libsparseir using an AI assisted editor.
What began as simple practice turned out to provide a far better development experience than expected, so I ultimately decided to migrate fully to the Rust version.
The resulting codebase is close to forty thousand lines including tests and examples, and the majority of it was generated by AI over roughly two months, based on my natural language instructions.

Key Points from the Previous Article

The core argument of the previous article can be summarized as follows.

In particular, the absence of a standard package manager, compiler, and build system is extremely painful.

More concretely, the following issues were highlighted.

  • Package management: splitting libraries into reusable components and managing dependencies for distribution is difficult.
  • Build systems and ABI: fragmented build configurations and ABI incompatibilities make distribution hard.
  • Memory safety: it is easy to write dangerous code, and such issues are difficult to eliminate mechanically.
  • Matrices, arrays, and documentation: weak de facto standards increase operational costs.

Why I Migrated to Rust

The greatest advantage of Rust lies in how easily results can be shared through its package system.
In computational strongly correlated physics, large C++ projects often become monolithic because there is no integrated package system.
In contrast, Rust automatically resolves dependencies, making it easy to publish small independent crates and reuse results across communities.

Rust also integrates cleanly with Python, Julia, and Fortran through a C API.
Wrapper libraries can reproducibly build shared libraries by invoking cargo internally.
This is far simpler than managing complex CMake based builds in C++, especially in restricted environments.

When using FFI, memory safety on the backend side is crucial.
In C++, segmentation faults are common and difficult to debug.
In Rust, careful auditing of unsafe blocks and FFI boundaries is usually sufficient.

Although Rust is known for its steep learning curve, AI assisted development changes this balance.
AI can quickly fix syntax errors and propagate structural changes across a large codebase.
In this sense, Rust benefits greatly from the rise of generative AI.

Challenges

Remaining challenges include integration with automatic differentiation systems in Python and Julia, and wrapping high performance computing libraries such as ScaLAPACK.
With generative AI assistance, these challenges appear manageable.

Division of Roles with Julia

Rust is well suited for robust foundational libraries, while Julia excels at rapid experimentation.
Because the two can be easily connected via C FFI, combining them allows each to play to its strengths.

In my view, a productive workflow is to prototype algorithms in Julia and migrate stabilized components to Rust to ensure robustness, reproducibility, and reuse across languages.

Updated: