{"id":247,"date":"2020-10-21T12:48:42","date_gmt":"2020-10-21T12:48:42","guid":{"rendered":"https:\/\/blog.mozilla.org\/data\/?p=247"},"modified":"2020-11-19T13:57:47","modified_gmt":"2020-11-19T13:57:47","slug":"this-week-in-glean-cross-platform-language-binding-generation-with-rust-and-uniffi","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/data\/2020\/10\/21\/this-week-in-glean-cross-platform-language-binding-generation-with-rust-and-uniffi\/","title":{"rendered":"This Week in Glean: Cross-Platform Language Binding Generation with Rust and \u201cuniffi\u201d"},"content":{"rendered":"<p><em>(\u201cThis Week in Glean&#8221; is a series of blog posts that the Glean Team at Mozilla is using to try to communicate better about our work. They could be release notes, documentation, hopes, dreams, or whatever: so long as it is inspired by Glean. You can find an<a href=\"https:\/\/mozilla.github.io\/glean\/book\/appendix\/twig.html\"> index of all TWiG posts online<\/a>.)<\/em><\/p>\n<p>As the <a href=\"https:\/\/github.com\/mozilla\/glean\/\">Glean SDK<\/a> continues to expand its features and functionality, it has also continued to expand the number and types of consumers within the Mozilla ecosystem that rely on it for collection and transport of important metrics.\u00a0 On this particular adventure, I find myself once again working on one of these components that tie into the Glean ecosystem.\u00a0 In this case, it has been my work on the <a href=\"https:\/\/github.com\/mozilla\/nimbus-sdk\">Nimbus SDK<\/a> that has inspired this story.<\/p>\n<p>Nimbus is our new take on a rapid experimentation platform, or a way to try out new features in our applications for subsets of the population of users in a way in which we can measure the impact.\u00a0 The idea is to find out what our users like and use so that we can focus our efforts on the features that matter to them.\u00a0 Like Glean, Nimbus is a cross-platform client SDK intended to be used on Android, iOS, and all flavors of Desktop OS that we support.\u00a0 Also like Glean, this presented us with all of the challenges that you would normally encounter when creating a cross-platform library.\u00a0 Unlike Glean, Nimbus was able to take advantage of some tooling that wasn\u2019t available when we started Glean, namely: <a href=\"https:\/\/github.com\/mozilla\/uniffi-rs\">uniffi<\/a>.<\/p>\n<p>So what is uniffi?\u00a0 It\u2019s a multi-language bindings generator for <a href=\"https:\/\/www.rust-lang.org\/\">Rust<\/a>.\u00a0 What exactly does that mean?\u00a0 Typically you would have to write something in Rust and create a hand-written <a href=\"https:\/\/en.wikipedia.org\/wiki\/Foreign_function_interface\">Foreign Function Interface (FFI)<\/a> layer also in Rust.\u00a0 On top of that, you also end up creating a hand-written wrapper in each and every language that is supported.\u00a0 Instead, uniffi does most of the work for us by generating the plumbing necessary to transport data across the FFI, including the specific language bindings, making it a little easier to write things once and a lot easier to maintain multiple supported languages.\u00a0 With uniffi we can write the code once in Rust, and then generate the code we need to be able to reuse these components in whatever language (currently supporting Kotlin, Swift and Python with C++ and JS coming soon) and on <a href=\"https:\/\/doc.rust-lang.org\/nightly\/rustc\/platform-support.html\">whatever platform<\/a> we need.<\/p>\n<p>So how does uniffi work?\u00a0 The magic of uniffi works through generating a <a href=\"https:\/\/doc.rust-lang.org\/edition-guide\/rust-2018\/platform-and-target-support\/cdylib-crates-for-c-interoperability.html?highlight=cdylib#cdylib-crates-for-c-interoperability\">cdylib<\/a> crate from the Rust code.\u00a0 The interface is defined in a separate file through an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Interface_description_language\">Interface Description Language (IDL)<\/a>, specifically, a variant of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Web_IDL\">WebIDL<\/a>.\u00a0 Then, using the uniffi-bindgen tool, we can scaffold the Rust side of the FFI and build our Rust code as we normally would, producing a shared library.\u00a0 Back to uniffi-bindgen again to then scaffold the language bindings side of things, either Kotlin, Swift, or Python at the moment, with JS and C++ coming soon.\u00a0 This leaves us with platform specific libraries that we can include in applications that call through the FFI directly into the Rust code at the core of it all.<\/p>\n<p>There are some limitations to what uniffi can accomplish, but for most purposes it handles the job quite well.\u00a0 In the case of Nimbus, it worked amazingly well because Nimbus was written keeping uniffi language binding generation in mind (and uniffi was written with Nimbus in mind).\u00a0 As part of playing around with uniffi, I also experimented with how we could leverage it in Glean. It looks promising for generating things like <a href=\"https:\/\/github.com\/mozilla\/glean\/tree\/main\/glean-core\/src\/metrics\">our metric types<\/a>, but we still have some state in the language binding layer that probably needs to be moved into the Rust code before Glean could move to using uniffi.\u00a0 Cutting down on all of the handwritten code is a huge advantage because the Glean metric types require a lot of boilerplate code that is basically duplicated across all of the different languages we support.\u00a0 Being able to keep this down to just the Rust definitions and IDL, and then generating the language bindings would be a nice reduction in the burden of maintenance.\u00a0 Right now if we make a change to a metric type in Glean, we have to touch every single language binding: Rust, Kotlin, Swift, Python, C#, etc.<\/p>\n<p>Looking back at Nimbus, uniffi does save a lot on overhead since we can write almost everything in Rust.\u00a0 We will have a little bit of functionality implemented at the language layer, namely a callback that is executed after receiving and processing the reply from the server, the threading implementation that ensures the networking is done in a background thread, and the integration with Glean (at least until the Glean Rust API is available).\u00a0 All of these are ultimately things that could be done in Rust as uniffi\u2019s capabilities grow, making the language bindings basically just there to expose the API.\u00a0 Right now, Nimbus only has a Kotlin implementation in support of our first customer, <a href=\"https:\/\/github.com\/mozilla-mobile\/fenix\/\">Fenix<\/a>, but when it comes time to start supporting iOS and desktop, it should be as simple as just generating the bindings for whatever language that we want (and that uniffi supports).<\/p>\n<p>Having worked on cross-platform tools for the last two years now, I can really appreciate the awesome power of being able to leverage the same client SDK implementation across multiple platforms.\u00a0 Not only does this come as close as possible to giving you the same code driving the way something works across all platforms, it makes it a lot easier to trust that things like Glean collect data the same way across different apps and platforms and that Nimbus is performing randomization calculations the same across platforms and apps.\u00a0 I have worked with several cross-platform technologies in my career like Xamarin or Apache Cordova, but Rust really seems to work better for this without as much of the overhead.\u00a0 This is especially true with tools like uniffi to facilitate unlocking the cross-platform potential.\u00a0 So, in conclusion, if you are responsible for cross-platform applications or libraries or are interested in creating them, I strongly urge you to think about <a href=\"https:\/\/www.rust-lang.org\/\">Rust<\/a> (there\u2019s no way I have time to go into all the cool things Rust does\u2026) and tools like <a href=\"https:\/\/github.com\/mozilla\/uniffi-rs\">uniffi<\/a> to make that easier for you.\u00a0 (If uniffi doesn\u2019t support your platform\/language yet, then I\u2019m also happy to report that it is accepting contributions!)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(\u201cThis Week in Glean&#8221; is a series of blog posts that the Glean Team at Mozilla is using to try to communicate better about our work. They could be release &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/data\/2020\/10\/21\/this-week-in-glean-cross-platform-language-binding-generation-with-rust-and-uniffi\/\">Read more<\/a><\/p>\n","protected":false},"author":1757,"featured_media":197,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[315988],"tags":[448297,448315,16179,448314],"coauthors":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts\/247"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/users\/1757"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/comments?post=247"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts\/247\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/media\/197"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/media?parent=247"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/categories?post=247"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/tags?post=247"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/coauthors?post=247"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}