3 This document describes how to use FFI in Firefox to get Rust code and C++ code to interoperate.
7 Generally speaking, the more complicated is the data you want to transfer, the
8 harder it'll be to transfer across the FFI boundary.
10 Booleans, integers, and pointers cause little trouble.
11 - C++ `bool` matches Rust `bool`
12 - C++ `uint8_t` matches Rust `u8`, `int32_t` matches Rust `i32`, etc.
13 - C++ `const T*` matches Rust `*const T`, `T*` matches Rust `*mut T`.
15 Lists are handled by C++ `nsTArray` and Rust `ThinVec`.
17 For strings, it is best to use the `nsstring` helper crate. Using a raw pointer
18 plus length is also possible for strings, but more error-prone.
20 If you need a hashmap, you'll likely want to decompose it into two lists (keys
21 and values) and transfer them separately.
23 Other types can be handled with tools that generate bindings, as the following
26 ## Accessing C++ code and data from Rust
28 To call a C++ function from Rust requires adding a function declaration to Rust.
29 For example, for this C++ function:
33 bool UniquelyNamedFunction(const nsCString* aInput, nsCString* aRetVal) {
38 add this declaration to the Rust code:
41 pub fn UniquelyNamedFunction(input: &nsCString, ret_val: &mut nsCString) -> bool;
45 Rust code can now call `UniquelyNamedFunction()` within an `unsafe` block. Note
46 that if the declarations do not match (e.g. because the C++ function signature
47 changes without the Rust declaration being updated) crashes are likely. (Hence
50 Because of this unsafety, for non-trivial interfaces (in particular when C++
51 structs and classes must be accessed from Rust code) it's common to use
52 [rust-bindgen](https://github.com/rust-lang/rust-bindgen), which generates Rust
53 bindings. The documentation is
54 [here](https://rust-lang.github.io/rust-bindgen/).
56 ## Accessing Rust code and data from C++
58 A common option for accessing Rust code and data from C++ is to use
59 [cbindgen](https://github.com/eqrion/cbindgen), which generates C++ header
60 files. for Rust crates that expose a public C API. cbindgen is a very powerful
61 tool, and this section only covers some basic uses of it.
65 First, add suitable definitions to your Rust. `#[no_mangle]` and `extern "C"`
70 pub unsafe extern "C" fn unic_langid_canonicalize(
72 ret_val: &mut nsCString
74 ret_val.assign("new value");
79 Then, add a `cbindgen.toml` file in the root of your crate. It may look like this:
82 header = """/* This Source Code Form is subject to the terms of the Mozilla Public
83 * License, v. 2.0. If a copy of the MPL was not distributed with this
84 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */"""
85 autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */
86 #ifndef mozilla_intl_locale_MozLocaleBindings_h
87 #error "Don't include this file directly, instead include MozLocaleBindings.h"
90 include_version = true
95 # Put FFI calls in the `mozilla::intl::ffi` namespace.
96 namespaces = ["mozilla", "intl", "ffi"]
98 # Export `ThinVec` references as `nsTArray`.
100 "ThinVec" = "nsTArray"
103 Next, extend the relevant `moz.build` file to invoke cbindgen.
106 if CONFIG['COMPILE_ENVIRONMENT']:
107 CbindgenHeader('unic_langid_ffi_generated.h',
108 inputs=['/intl/locale/rust/unic-langid-ffi'])
110 EXPORTS.mozilla.intl += [
111 '!unic_langid_ffi_generated.h',
115 This tells the build system to run cbindgen on
116 `intl/locale/rust/unic-langid-ffi` to generate `unic_langid_ffi_generated.h`,
117 which will be placed in `$OBJDIR/dist/include/mozilla/intl/`.
119 Finally, include the generated header into a C++ file and call the function.
122 #include "mozilla/intl/unic_langid_ffi_generated.h"
124 using namespace mozilla::intl::ffi;
126 void Locale::MyFunction(nsCString& aInput) const {
128 unic_langid_canonicalize(aInput, &result);
134 Many complex Rust types can be exposed to C++, and cbindgen will generate
135 appropriate bindings for all `pub` types. For example:
139 pub enum FluentPlatform {
148 pub fn FluentBuiltInGetPlatform() -> FluentPlatform;
153 ffi::FluentPlatform FluentBuiltInGetPlatform() {
154 return ffi::FluentPlatform::Linux;
158 For an example using cbindgen to expose much more complex Rust types to C++,
159 see [this blog post].
161 [this blog post]: https://crisal.io/words/2020/02/28/C++-rust-ffi-patterns-1-complex-data-structures.html
165 If you need to create and destroy a Rust struct from C++ code, the following
166 example may be helpful.
168 First, define constructor, destructor and getter functions in Rust. (C++
169 declarations for these will be generated by cbindgen.)
173 pub unsafe extern "C" fn unic_langid_new() -> *mut LanguageIdentifier {
174 let langid = LanguageIdentifier::default();
175 Box::into_raw(Box::new(langid))
179 pub unsafe extern "C" fn unic_langid_destroy(langid: *mut LanguageIdentifier) {
180 drop(Box::from_raw(langid));
184 pub unsafe extern "C" fn unic_langid_as_string(
185 langid: &mut LanguageIdentifier,
186 ret_val: &mut nsACString,
188 ret_val.assign(&langid.to_string());
192 Next, in a C++ header define a destructor via `DefaultDelete`.
195 #include "mozilla/intl/unic_langid_ffi_generated.h"
196 #include "mozilla/UniquePtr.h"
201 class DefaultDelete<intl::ffi::LanguageIdentifier> {
203 void operator()(intl::ffi::LanguageIdentifier* aPtr) const {
204 unic_langid_destroy(aPtr);
208 } // namespace mozilla
211 (This definition must be visible any place where
212 `UniquePtr<intl::ffi::LanguageIdentifier>` is used, otherwise C++ will try to
213 free the code, which might lead to strange behaviour!)
215 Finally, implement the class.
220 explicit Locale(const nsACString& aLocale)
221 : mRaw(unic_langid_new()) {}
223 const nsCString Locale::AsString() const {
225 unic_langid_as_string(mRaw.get(), &tag);
230 UniquePtr<ffi::LanguageIdentifier> mRaw;
234 This makes it possible to instantiate a `Locale` object and call `AsString()`,
239 For a detailed explanation of an interface in Firefox that doesn't use cbindgen
240 or rust-bindgen, see [this blog post](https://hsivonen.fi/modern-cpp-in-rust/).