Why Fish Shell Switched from C++ to Rust: Lessons from a Two‑Year Rewrite

The Fish Shell project migrated its entire codebase from C++ to Rust over two years, detailing the motivations, challenges, mistakes, and benefits of the transition, and outlining the future work still pending for this open‑source shell.

21CTO
21CTO
21CTO
Why Fish Shell Switched from C++ to Rust: Lessons from a Two‑Year Rewrite

Background: Why Rewrite Fish Shell in Rust?

About two years ago, Fish Shell core maintainer @ridiculousfish opened a pull request titled “#9512 – Rewrite it in Rust”. What began as a joke quickly attracted community attention, and with the successful release of Fish 4.0 beta the shell abandoned C++ entirely, rewriting almost all code in Rust.

Fish Shell had previously moved from pure C to C++. This time, moving from C++ to Rust was more ambitious because Rust did not exist when Fish Shell was first created in 2007.

Problems Encountered with C++

Toolchain and compiler/platform differences: C++ toolchains were immature, especially for older systems (LTS Linux, older macOS). Without tools like rustup, installing the latest C++ compiler was complex, adding burden for packagers and contributors.

Usability and safety: C++ syntax and features (templates, headers) often caused compilation errors and unsafe operations. Multithreading was particularly tricky, leading to cross‑thread object‑sharing bugs that could only be caught by post‑mortem tools such as Thread Sanitizer.

Low community participation: Over eleven years only 17 people made more than ten commits to the C++ codebase, a worrying sign for an open‑source project that relies on community contributions.

Performance vs. usability trade‑offs: While C++ emphasized performance, many modern features (e.g., string_view) introduced safety hazards like use‑after‑free.

Challenges and Mistakes During the Transition

The team initially tried complex macros to simplify string handling, only to replace them later with the more conventional L!("foo") macro. Misunderstanding original code details also caused bugs that crashed the program, often due to using assert! or its modern counterpart .unwrap() for error handling.

A notable issue involved the time_t type in the libc crate. The team added wrappers to avoid a future deprecation warning about time_t becoming 64‑bit, later realizing the warning was irrelevant because Fish Shell never passes time_t between different C libraries. This taught them that over‑optimisation can add unnecessary complexity.

Enabling Link‑Time Optimization (LTO) as the default release build in CMake unintentionally increased build times, inconveniencing developers. Despite these setbacks, the team eventually found solutions and kept the project on track.

Gains and Improvements

Rust’s toolchain and ecosystem brought clear benefits. Developers no longer depend on a complex C++ toolchain; rustup allows quick installation and upgrades, simplifying the development workflow. Rust’s compiler messages are friendly, helping developers locate and fix issues faster.

The rewrite also eliminated the dependency on the curses library. In the C++ version, curses was used for terminal information (terminfo), causing cross‑platform installation headaches. The Rust rewrite uses a dedicated crate to handle terminfo, removing the need for manual curses installation; Cargo automatically fetches and builds the required dependencies. Fish Shell still reads terminfo files, so users can provide them at runtime or rely on the built‑in xterm-256color definition.

Another major improvement is the ability to create “self‑install” packages that embed all functions, completions, and resources into a single binary. This enables static‑linked builds, especially useful on Linux. By linking against musl instead of glibc, the team avoided crashes that sometimes occur with glibc. The resulting self‑contained binary can run on remote servers without root privileges, simply by SSH‑ing in, greatly improving user convenience.

Unfinished Work and Future Outlook

Although the transition was largely successful, some goals remain unmet. The team hoped to drop CMake entirely, but Cargo’s limitations for installing scripts, documentation, and test suites mean CMake is still required, albeit in a much simplified form. They may later replace CMake with tools like just or make, though this is not a current priority.

Cygwin support on Windows is currently missing because Rust does not yet target Cygwin. Windows users can still use Fish Shell via WSL (Windows Subsystem for Linux).

Final Outcome

After nearly two years of effort, Fish Shell completed its migration from C++ to Rust. The rewrite resolved many long‑standing technical issues and injected new energy and possibilities into the project. Rust’s safety, ergonomics, and powerful toolchain have made the codebase more robust and maintainable.

While the journey was not without obstacles, continuous learning and adjustments allowed the team to achieve their goals. Looking ahead, Fish Shell will continue exploring innovative features and improving the user experience.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Rustopen sourcefish-shelltoolchainC++language migration
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.