Integrating a Rust library to Gecko / Firefox is unexpectedly easy, but Rust adoption varies significantly between teams. I started in the Firefox networking Team Necko, which has a few Rust components like the HTTP/3 stack neqo or the URL implementation. After switching to Privacy / Anti-Tracking team I'm now in a team that has no Rust code at all. The interest for Rust components is there, but usually projects are started in C++ due to all code being in C++.
Rust in Gecko
Looking at current language stats, we can see that Rust is currently at around 12% of Firefox code with almost 5 million lines of code. This is not neglectable, but falls short behind C / C++ and also JavaScript.
Note: Using tokei with
--exclude=third_partysuggests Rust has about 8% of the LOC of C++ for code written in mozilla-firefox directly. Not 50% as this diagram might suggest. (feedback after lightning talk)
Error categories rules out by Rust
- Use after free bugs: No more
kungFuDeathGripsecurity fixes to extend the lifetime of an object. - Data races: No more forgetting to question "Do I need to lock a Mutex to access this variable?"
- Nullptr dereferences: Call by pointer is very common in C++, especially in idl interfaces. "Can this pointer I’ve got passed be null?" is now answered by the Rust type system.
- Iterator invalidation: Another big class of errors. In Gecko this is also memory safe for nsTArray, but wrong code.
- Other undefined behavior
Other Rusty reasons
In my opinion, Rust is just a very well-designed language. It has great documentation infrastructure and awesome compiler errors aiding you in writing safe code.
Case Study: RIIR "Copy Clean Link"
The patch for the case study can be found in D225390.
Almost always you probably want to split up the library into two parts:
One crate for functionality and bindings. The functionality crate can be developed out-of-tree. In this example I'm going to have the crate be developed in-tree.
Add crate directories to moz.build
Add directory where the crates are located need to be included in the parent moz.build:
DIRS += [
"urlstrip",
"urlstrip_glue",
]
Functionality crate
The crate implementing the logic with no dependency on xpcom types simply needs to have a single-line (plus license text) moz.build next to the Cargo.toml:
FINAL_LIBRARY = "xul"
Note: To combat build failue you need to regenerate the
Cargo.tomlby running./mach vendor rust --allow-dirty.
cbindgen / XPCOM bindings crate
You need a few more lines for the bindings crate to autogenerate a C++ header with cbindgen.
FINAL_LIBRARY = "xul"
EXPORTS.mozilla += [
"UrlStrip.h",
]
if CONFIG["COMPILE_ENVIRONMENT"]:
CbindgenHeader(
"UrlStrip_ffi.h",
inputs=["/toolkit/components/antitracking/urlstrip_glue"]
)
EXPORTS.mozilla += [
"!UrlStrip_ffi.h",
]
You can depend on nsstring and other xpcom crates. However, you won't be able to use Rust's native #[test] directive. This is one of the reasons you likely really want to have the separation between the two crate types.
To configure cbindgen, get inspired by taking a look at other cbindgen.toml files.
You also want to have somewhat nicer C++ headers around your autogenerated cbindgen header for C++ UrlStrip.h:
struct UrlStrip {
public:
static void Create(UrlStrip** pThis) { urlstrip_new(pThis); }
void Delete() { urlstrip_drop(this); }
void Init() { urlstrip_init(this); };
void Uninit() { urlstrip_uninit(this); };
nsresult Strip(const nsACString& aURI, nsCString* aOutURI) {
return urlstrip_strip(this, &aURI, aOutURI);
}
bool CanStrip(const nsACString& aURI) {
return urlstrip_canstrip(this, &aURI);
}
private:
UrlStrip() = delete;
~UrlStrip() = delete;
UrlStrip(const UrlStrip&) = delete;
UrlStrip& operator=(const UrlStrip&) = delete;
};
Maybe cxx could ease with this in the future: Bug 1921139 - Allow creating C++ bindings for Rust code with cxx.
Other files to touch
To use the Rust library from C++/JS, it must be linked. And for that you need to both add it as a dependency to toolkit/library/rust/shared/Cargo.toml:
urlstrip = { path = "../../../components/antitracking/urlstrip" }
urlstrip_glue = { path = "../../../components/antitracking/urlstrip_glue" }
And also important (due to still using 2015 rust edition) extern crate definitions in toolkit/library/rust/shared/lib.rs:
extern crate urlstrip;
extern crate urlstrip_glue;
Done
This is all. Feel free to reach out to me if you have any questions!
Further Resources
- https://firefox-source-docs.mozilla.org/build/buildsystem/rust.html
- https://firefox-source-docs.mozilla.org/writing-rust-code/
- Blog as Lightning Talk
- Lightning Talk slides
Updates
- 2025-07-10: Added note on Rust usage within gecko
- 2025-11-02: note on how to combat build failure after adding rust crate
