Bug 1885489 - Part 9: Add SnapshotIterator::readObject(). r=iain
[gecko.git] / docs / writing-rust-code / xpcom.md
blobdbe297e368fc3dbfa74be9f488e5d5505f3c40f9
1 # XPCOM components in Rust
3 XPCOM components can be written in Rust.
5 ## A tiny example
7 The following example shows a new type that implements `nsIObserver`.
9 First, create a new empty crate (e.g. with `cargo init --lib`), and add the
10 following dependencies in its `Cargo.toml` file.
12 ```toml
13 [dependencies]
14 libc = "0.2"
15 nserror = { path = "../../../xpcom/rust/nserror" }
16 nsstring = { path = "../../../xpcom/rust/nsstring" }
17 xpcom = { path = "../../../xpcom/rust/xpcom" }
18 ```
20 (The number of `../` occurrences will depend on the depth of the crate in the
21 file hierarchy.)
23 Next hook it into the build system according to the [build
24 documentation](/build/buildsystem/rust.rst).
26 The Rust code will need to import some basic types. `xpcom::interfaces`
27 contains all the usual `nsI` interfaces.
29 ```rust
30 use libc::c_char;
31 use nserror::nsresult;
32 use std::sync::atomic::{AtomicBool, Ordering};
33 use xpcom::{interfaces::nsISupports, RefPtr};
34 ```
36 The next part declares the implementation.
38 ```rust
39 #[xpcom(implement(nsIObserver), atomic)]
40 struct MyObserver {
41     ran: AtomicBool,
43 ```
45 This defines the implementation type, which will be refcounted in the specified
46 way and implement the listed xpidl interfaces. It will also declare a second
47 initializer struct `InitMyObserver` which can be used to allocate a new
48 `MyObserver` using the `MyObserver::allocate` method.
50 Next, all interface methods are declared in the `impl` block as `unsafe` methods.
52 ```rust
53 impl MyObserver {
54     #[allow(non_snake_case)]
55     unsafe fn Observe(
56         &self,
57         _subject: *const nsISupports,
58         _topic: *const c_char,
59         _data: *const u16,
60     ) -> nsresult {
61         self.ran.store(true, Ordering::SeqCst);
62         nserror::NS_OK
63     }
65 ```
67 These methods always take `&self`, not `&mut self`, so we need to use interior
68 mutability: `AtomicBool`, `RefCell`, `Cell`, etc. This is because all XPCOM
69 objects are reference counted (like `Arc<T>`), so cannot provide exclusive access.
71 XPCOM methods are unsafe by default, but the
72 [xpcom_method!](https://searchfox.org/mozilla-central/source/xpcom/rust/xpcom/src/method.rs)
73 macro can be used to clean this up. It also takes care of null-checking and
74 hiding pointers behind references, lets you return a `Result` instead of an
75 `nsresult,` and so on.
77 To use this type within Rust code, do something like the following.
79 ```rust
80 let observer = MyObserver::allocate(InitMyObserver {
81   ran: AtomicBool::new(false),
82 });
83 let rv = unsafe {
84   observer.Observe(x.coerce(),
85                    cstr!("some-topic").as_ptr(),
86                    ptr::null())
88 assert!(rv.succeeded());
89 ```
91 The implementation has an (auto-generated) `allocate` method that takes in an
92 initialization struct, and returns a `RefPtr` to the instance.
94 `coerce` casts any XPCOM object to one of its base interfaces; in this case,
95 the base interface is `nsISupports`. In C++, this would be handled
96 automatically through inheritance, but Rust doesn’t have inheritance, so the
97 conversion must be explicit.
99 ## Bigger examples
101 The following XPCOM components are written in Rust.
103 - [kvstore](https://searchfox.org/mozilla-central/source/toolkit/components/kvstore),
104   which exposes the LMDB key-value store (via the [Rkv
105   library](https://docs.rs/rkv)) The API is asynchronous, using `moz_task` to
106   schedule all I/O on a background thread, and supports getting, setting, and
107   iterating over keys.
108 - [cert_storage](https://searchfox.org/mozilla-central/source/security/manager/ssl/cert_storage),
109   which stores lists of [revoked intermediate certificates](https://blog.mozilla.org/security/2015/03/03/revoking-intermediate-certificates-introducing-onecrl/).
110 - [bookmark_sync](https://searchfox.org/mozilla-central/source/toolkit/components/places/bookmark_sync),
111   which [merges](https://mozilla.github.io/dogear) bookmarks from Firefox Sync
112   with bookmarks in the Places database.
113   [There's also some docs on how Rust interacts with Sync](/services/sync/rust-engines.rst)
114 - [webext_storage_bridge](https://searchfox.org/mozilla-central/source/toolkit/components/extensions/storage/webext_storage_bridge),
115   which powers the WebExtension storage.sync API. It's a self-contained example
116   that pulls in a crate from application-services for the heavy lifting, wraps
117   that up in a Rust XPCOM component, and then wraps the component in a JS
118   interface. There's also some boilerplate there around adding a
119   `components.conf` file, and a dummy C++ header that declares the component
120   constructor. [It has some in-depth documentation on how it hangs together](../toolkit/components/extensions/webextensions/webext-storage.rst).