Introduction to Robius

Robius is a fully open-source, decentralized, community-driven effort to enable multi-platform application development in Rust.

The Robius organization also acts as an informal1 working group: a welcoming, public space to collect and discuss resources related to improving and furthering the app dev experience in Rust.

Got a question? Want to get involved? Interested in contributing?
Come join our friendly community on the Matrix chat Robius space.

Our vision

We believe that the Rust programming language is the right choice for the next generation of application developers, but that the language ecosystem needs a bit more love and attention to make it a first-class citizen in the world of application development, particularly on mobile platforms.

We envision a future in which:

  1. Rust developers can create safe, beautiful, and robust applications that execute efficiently and performantly on a wide variety of platforms, especially mobile.
  2. Frontend developers using other languages are encouraged to come over to the dark side wonderful world of Rust and give it a try, with a seamless transition experience that helps overcome the steep learning curve commonly associated with Rust.
  3. The Rust ecosystem is broadened and strengthened, demonstrating to existing Rust experts in other domains that Rust is a great fit for application development, not just low-level systems and embedded programming.

What's in a name?

The name Robius comes from the Latin word robius, meaning red in color, as in oxen, wheat, rust, etc. This makes for a nice color-based connection to Rust, our programming language of choice.

Robius rhymes with Mobius, and our logo/icons take inspiration from the wordplay combination of "Rust" and "Mobius strip".

Yes, technically, the original German name is Möbius, but we use an Americanized pronunciation with a long "o" sound: "Roe-bee-us" / ˈɹoʊˈbiəs.

Key Community Projects

The Robius ecosystem consists of several independent projects that can be composed into a complete system stack to realize fast, painless application development across multiple platforms in pure Rust. Components are loosely coupled, allowing a developer (in the future) to customize which components are used to comprise the underlying system, such as choosing

  • Makepad is a cross-platform UI toolkit currently under active development that offers a hybrid retained-mode and immediate-mode UI model.

    • Rapid development cycle: very fast compile times due to a custom minimal dependency set, plus a custom DSL for live design that enables hot reloading of UI elements.
    • Makepad Studio: an IDE prototype built using Makepad itself with unique features like cross-process shared textures for live reload of an in-window UI app, docking tabs for file/window views, hyper-smooth code folding, and more.
    • Makepad framework: a (growing) collection of highly-performant widgets and minimal, zero/low-overhead platform abstractions.
  • Dioxus is a cross-platform, production-ready UI toolkit that is inspired by React, with a custom metalanguage called RSX that is used to declare UI elements/layout in an HTML-like style.

    • Supports many platforms with a set of interchangeable target renderers, including desktop, webapps, static sites, text UIs, liveview, and mobile.
    • Fast and memory-efficient, with perfect lighthouse scores and performance orders of magnitude better than Node or Python.
    • Excellent built-in abstractions for state management.
    • Easy, familiar styling using vanilla CSS or the CSS framework of your choice, e.g., Tailwind.
  • Osiris is a set of Rust interfaces for developing immersive applications atop a diverse set of operating systems services and platform-specific functionality.

    • Osiris aims to provide Rust apps with an easy canonical way to access platform features like storage, networking, multimedia (video, audio, camera), geolocation, device orientation (accelerometer, gyro), timers & alarms, notifications, clipboard, drag-n-drop, and more.
    • osi: the primary Rust package that exposes direct access to OS interfaces. Higher-level Rust abstractions are coming soon, after raw interfaces for more platforms are complete.
    • Osiris offers build tooling that can:
      1. Set up new project directories with auto-generated scaffolding for platform-specific integration components that can be customized post-creation.
      2. Generate application artifacts that adhere to the policies of each platform, i.e., packages that can be published to common app stores (Google Play, Apple App Store, Microsoft Store, etc).
  • ylong is an async runtime for Rust that includes:

    • Priority-based scheduling of tasks.
    • Abstractions for auto-parallelizing computation, offering parallel iterators over data in a manner similar to rayon, but for async tasks rather than native (OS-level) threads.
    • Standard primitives for non-blocking async I/O, async synchronization, and async timers.
    • An executor and reactor to schedule tasks and respond to system events, respectively.

Repositories of Interest

Robius aims to provide a fully-functional reference design of the entire system stack beneath the application, for which an architectural overview and detailed documentation will be available.

We also intend to provide two classes of actual applications:

  1. Flagships: complete, fully-featured apps with a clean UI design, polished UX, and functional business logic. These apps will be publishable to platform app stores.
  2. Simple demos: a series of basic example apps that exhibit a few key features, with mock components underneath and elsewhere.

Flagship apps

  • Robrix: the Robius Matrix chat client.
    • Currently under active development -- please check it out and contribute if you're interested!
    • The needs of this app will be the primary driver for Robius development.

Simple demo apps

For more examples, check out the extensive set of Dioxus example apps and Makepad example apps. Osiris-specific examples are coming soon.

Contributing

We welcome contributions, ideas, and suggestions from anyone! We're also open to help you host and maintain your project under the umbrella of the Robius organization.

Translations of book content and READMEs to other languages are especially appreciated! If you're multilingual and feeling generous, kindly reach out to us.

As mentioned before, please feel free to reach out to our friendly community of devs on the Robius space on Matrix chat.

1

Robius is not officially affiliated with the Rust project or Rust foundation.

Current status

Robius is a brand new vision -- we're just getting off the ground.

Currently, the best way to get started is to directly use one of the recommended UI toolkits to build out your application's UI and define its UX behavior. For now, everything else beyond UI will require you to add the missing pieces yourself, e.g., network connectivity, async multitasking, and access to other device peripherals or system services.

Everything is developed right here in the open, so check back for updates often! We plan to introduce a pre-alpha version of the full Robius system stack (everything beneath the application) by early-to-mid 2024, which will enable easier access to and integration of other platform/OS features alongside the UI toolkits.

Platform support

The following table indicates which projects in the Robius community currently support a given feature on each given platform.

The key/legend for this table is as follows:

  • MP : supported by Makepad.
  • DX : supported by Dioxus.
  • OS : supported by Osiris.
  • — : feature not applicable or not planned for the given platform.
  • Blank table cells indicate the feature has not been started.

Android iOS Linux MacOS Windows Web OpenHarmony
Basic build tool MP DX OS MP DX MP DX OS MP DX MP DX MP DX
Basic UI MP DX MP DX MP DX MP DX MP DX MP DX
UI live/hot reload MP MP MP DX MP DX MP DX MP DX
Multiwindow MP MP MP
Input Events MP MP MP MP MP MP
Timers, Alarms MP MP MP MP MP MP
Camera MP MP MP MP MP
Audio Input MP MP MP MP MP
Audio Playback
MIDI Output MP MP MP MP MP MP
Video Playback MP
Storage/Filesystem MP MP MP
Networking MP MP MP MP MP MP
Permissions mgmt
Geolocation
Clipboard (text)
MP MP MP MP MP MP
Notifications
Drag & Drop MP MP
Accelerometer/Gyro
Vibration/haptics
Biometric
WiFi mgmt
Bluetooth
Display brightness
Power/Battery state
Physical buttons
Proximity sensor
Ambient light sensor

Robius Whitepaper

This chapter contains an informal whitepaper about the Robius project, including:

  • The motivation behind Robius — why we chose to embark upon this journey.
  • Project goals — what we want to achieve.
  • Technical requirements — qualitative and quantitative metrics we must hit.
  • System Architecture — a reference design for the full system stack.

You can also view this whitepaper content as:

Elevator pitch — Robius in 60 seconds

The Robius project's primary focus is to provide a multi-platform application development toolkit where both the app and the entire system stack are written completely in Rust. This will enable developers across all layers to leverage the many benefits of Rust: safety, efficiency, fearless concurrency, high performance, and a robust ecosystem of crates spanning many domains. A Rust-only stack avoids the additional complexity brought on by introducing other languages via FFI interop layers; these "bridges" only facilitate the usage of Rust for business logic, and impose high overhead when crossing the boundary between Rust and the platform-native framework.

Robius's secondary focus is to make the Rust experience on mobile platforms feel like a first-class citizen. Historically, Rust on mobile has been a neglected area of the ecosystem, missing key components from build tooling to native-like UIs and widgets to platform support crates. We want to make Rust as enjoyable to use on mobile platforms as it is to use in systems programming environments.

Robius is and always will be fully open-source, decentralized, and community-driven.

What's in a name?

The name Robius comes from the Latin word robius, meaning red in color, as in oxen, wheat, rust, etc. This makes for a nice color-based connection to Rust, our programming language of choice.

Robius rhymes with Mobius, and our logo/icons take inspiration from the wordplay combination of "Rust" and "Mobius strip".

Yes, technically, the original German name is Möbius, but we use an Americanized pronunciation with a long "o" sound: "Roe-bee-us" / ˈɹoʊˈbiəs.

Motivation: why Robius?

Several motivating factors inspired us to start the Robius project:

  1. App developers want to use Rust, but aren’t sure where or how to get started due to Rust's app dev ecosystem being in a rough state.
    • Paralysis of choice among dozens of partial solutions
    • Unclear how to integrate many disparate projects

  1. There are clear business advantages to writing applications in a multi-platform Rust framework.
    • Provide a consistent experience for devs and customers
    • Avoid redundant dev effort → save money, faster time to market

  1. Rust is a great language and the right choice for many programming domains, but not for multi-platform apps ... yet.
    • Safety → increased correctness, reliability, lower debugging costs
    • High performance → responsive, jank-free UI
    • No runtime or garbage collection → energy efficiency on mobile devices

The following sections examine each of these factors in detail.

1. Developers really want to write apps in Rust

Online programming forums are filled with posts asking how to use Rust to develop mobile, desktop, and web applications. We have observed this trend for several years in two main flavors:

  • Existing Rust systems-level developers that wonder if they can leverage their Rust knowledge for applications too.
  • Frontend app developers that currently use other languages, but want to use Rust because they have heard about its benefits.

The former group of Rust devs is mainly focused on the biggest unknown to them: creating GUI apps and packaging apps up for mobile targets; this is the first group that we wish to support with Robius. The latter group of frontend devs have deeper expertise in application-level implementation but are not sure how to transition those skills to the world of Rust; this is the second group that we wish to support. One major commonality is that both groups are unsure how or where to start developing an app in Rust.

Ecosystem is fractured and incomplete

The first problem that most aspiring devs encounter is the massive collection of projects, mostly GUI libraries, many of which are incomplete proofs-of-concept or simply abandoned. Though there are many competing UI frameworks in the main crates.io registry, it is difficult to discover which ones are most appropriate for a given application's needs, and even harder to determine whether and how these projects can be integrated with others to form a functional app dev toolkit.

For more evidence, look no further than the areweguiyet.org website, which aims to track the bevy of GUI crates and their progress. Their homepage states:

As a low level language, Rust is perfectly suitable for making user interfaces the old fashioned way, with native APIs. However, competing in today's world typically means supporting many platforms, and that makes using native APIs an unattractive option for many.

Rust's expressiveness and high level abstractions make it ideal for building intricate and complex user interfaces. Unfortunately, there is little consensus on what the best abstractions are.

There are a number of bindings available today to existing frameworks, but those looking for a mature, easy to use, and completely Rust-based solution will most likely find themselves out of luck.

Due to the relatively young age of the Rust language itself, many of these crates are incomplete, as building a UI toolkit is a lengthy endeavor. Most are only proofs-of-concept that offer barebones UI primitives; even the more mature and fully-developed crates only offer a partial solution for application development, focusing primarily on the UI appearance and fundamental UX behavior.

As expected, there is not yet a de facto "best" toolkit, nor has the Rust Foundation or Rust project teams made any official recommendation or guidebook concerning this matter. Although this is good for the sake of both neutrality and healthy competition, it has led to a fractured ecosystem in which nearly every crate has significant overlap with the functionality of several other crates. This overlap makes it even harder to select a UI toolkit for a new app, as the differences between toolkits are minimal on paper and only become evident after several weeks of development experience with each one.

All in all, there is a noticeable absence of cohesion and coordination in this ecosystem, presenting the most significant barrier of entry to cross-platform application development in Rust. This is especially evident when compared to app dev frameworks led by a single directing entity, such as React Native or Flutter.

Apps need more than just UI + UX

Even if GUI development in Rust was a solved problem, UIs do not tell the whole story — an application still needs to access services and features from the underlying platform or OS. As with UI works, there are indeed a plethora of crates that offer basic interfaces to access platform-specific features. Some offer general abstractions of a single feature across multiple platforms, while others or specific features on a single platform.

These features are crucial to developing immersive applications that take full advantage of the software and hardware functionality afforded to them by the device platform. For example, robust applications typically expect an app dev framework to expose the following:

  • camera, multimedia (audio and video) capture and playback,
  • geolocation, haptic feedback actuators like vibration motors,
  • system services like notifications, drag and drop, a rich clipboard,
  • on-device sensors like accelerometers/gyros, barometers, thermometers, proximity and ambient light sensors, etc,
  • local communication protocols like WiFi, Bluetooth, NFC, and more,
  • typical I/O like storage, cache space, and networking, in a platform-native manner.

While these crates can certainly be used directly by an application, this model places the serious burden of assembling multiple piecemeal crates into a workable platform abstraction on the shoulders of the application developers themselves. This expectation is not feasible for most application developers, as they are neither sufficiently experienced nor have the time to undertake such an arduous task.

That is why we created the Osiris project — to fill in the gaps and offer a fully-integrated, consistent solution for accessing platform-specific functionality in an abstract manner.

Documentation is paramount

Another reason many newcomers find it so difficult to get started is that there is a distinct lack of documentation, tutorials, and examples for the vast majority of crates in this domain.

If examples exist, they are extraordinarily simple and only cover the basics of setting up a primitive UI or application skeleton. Holistic tutorials and complete examples are few and hard to come by, which exacerbates the integration problem. Without end-to-end sample code, it is challenging for an expert, let alone a newcomer to the worlds of app dev and Rust, to compose a working system stack by integrating multiple disparate crates together.

One guiding light in this space is Dioxus, which has recently released high-quality comprehensive documentation for both external users and internal contributors along with several well-commented demo apps, making it both easy and enticing to use Dioxus to create new apps.

For a Rust application development framework to gain real traction, it must lead the pack in terms of documentation quality, tutorial thoroughness, and variety and depth of real-world example application code.

2. Rust app dev offers clear business advantages

There is a strong business case to be made for application development in Rust. Several existing commercial applications have already been "written in Rust," but Rust is only used to implement an internal business logic layer; the entire remainder of the app (e.g., frontend UI and UX) is written using different platform-specific native languages and SDKs. This leads to significant fragmentation and redundant development effort on each platform they wish to support, i.e., wasted expense.

One such example is Signal, a popular open-source messaging app focused on user privacy. As pictured below, Signal maintains multiple separate repositories for each of its client apps on Android, iOS, and desktop, as well as for its server. The Rust component of Signal is the shared libsignal crate, which implements the main Signal protocol functionality in a platform-agnostic manner with multiple bindings to other platform-native languages like Swift, Typescript, and Java.

Signal app architecture

Each of these app repositories are non-trivial in size: 450K+ LoC on iOS, 350K+ LoC on Android, and 250K+ LoC on desktop. The only reason the Signal desktop app is not even more complex and large is because it is built atop the Electron framework, which is known to be an inefficient resource hog with poor accessibility support. However, building desktop apps on Electron is still the default choice because it is easy and familiar, and at the time there were no viable alternatives to the much more expensive option of per-platform native apps on Windows, macOS, and Linux.

The main disadvantage of having multiple separate app repos is that it is incredibly challenging maintain consistency in both appearance and behavior. General maintainability also becomes more tedious, as bugs that manifest differently across platforms must be addressed on an individual basis. To their credit, Signal has actually done an impressive job of just that, with help from the community, though naturally some minor issues do slip through the cracks. For example, Signal on Android allows placing secure chats behind a screen lock (e.g., using biometrics), but desktop Signal does not offer that. Signal on Android cannot search only a single chat only, while other platforms support that filter. Mobile Signal apps offer contact-specific notifications, while desktop does not. The iOS app does not allow choice of fonts or changing font sizes and has different defaults than others, e.g., for embedded link previews, WiFi vs. cellular data usage, etc.


Another similar motivating example is Element, a first-party Matrix chat client that is also implemented separately on each platform, including Android, iOS, desktop, and web. Unfortunately, the maintenance burden has proven difficult to handle, with Element apps receiving moderately poor user reviews (3.8⭐ on Google Play, 3.5⭐ on Apple's App Store) due to inconsistent behavior, bugs, and slow performance. This undoubtedly stems from the effort required to accommodate the idiosyncrasies of each platform, which steals developer focus away from improving the main app-level features and experience.

Element themselves have acknowledged this issue and began developing a new set of applications called Element X, with a focus on sharing code across platforms via a shared Matrix Rust SDK. They are also exploring a new design system called Compound that aims to establish consistent UI component implementations shared across iOS, Android, and the web. However, both Compound and Element X apps on Android and iOS still exist separately and are implemented with platform-specific UI frameworks, e.g., Jetpack Compose and SwiftUI.

Element claims that the Element X apps are up to 6000 times faster than the legacy Element apps, due in part to Rust:

Our new Rust SDK makes development faster and more consistent across the Android and iOS apps, while providing a super-speedy and high quality implementation thanks to Rust’s legendary language safety features.

Although the new sliding sync protocol is likely responsible for the majority of those performance gains, this statement speaks to the attitude surrounding Rust — that switching to Rust will bring 💪 huge gains 💪.

In general, a business stands to gain several competitive advantages from using a cross-platform Rust app dev toolkit, as listed below. These mostly overlap with the benefits of switching to Flutter1, though as shown later, developers vastly prefer using Rust over Dart, Flutter's required language.

  • Ability to deliver a more consistent experience to customers across platforms.
  • Ease of attracting talented developers due to a modern stack and better team culture/morale1.
  • Elimination of redundant development effort across platforms, which:
    • Saves money due to higher productivity,
    • Results in a faster time to market, and
    • Allows developers to focus more on UI/UX quality.

In conclusion, these favorable Rust experiences, together with the challenges faced by Signal and Element with maintaining separate per-platform app repositories, clearly motivate the need for a multi-platform application development toolkit in Rust.

1

A survey by Very Good Ventures found that businesses reported these benefits when switching to Flutter for app development.

3. Rust is the right choice for the future of app dev

Both current trends and key aspects of Rust reinforce our assertion that Rust is the best choice for the next generation of multi-platform application development.

Rust has been voted the most admired language for the past 8 years in a row (2015 - 2023), according to the StackOverflow annual developer survey. For the first time, Rust also became the most wanted language this year (2023).

In addition, Rust is leading the development of technologies for new platforms, e.g., it is the most frequently used language for WebAssembly (WASM) for the third year running. Rust is highly used in WebAssembly projects The same study also found that:

  • Developers of WASM tools have a strong preference for Rust,
  • Rust is the most desired language for projects involving WASM, and
  • The leading and most-used WASM runtimes are written in Rust, the Wasmtime and wasmer projects.

These trends indicate that Rust adoption will continue to grow, and that other developers are open and willing to learn Rust in order to use it for new applications.

Rust combines safety with usability and performance

If you're reading this book, you likely already know about the strengths of Rust and the benefits that it provides. If not, we cover a select subset of those benefits here.

First, Rust's strong, static type system guards against type errors at compile time, enabling full type safety. This type safety leads to memory safety, which eliminates entire classes of bugs: use after free, double free, buffer overflow and underflow, reading uninitialized memory, concurrency violations like race conditions, and more.

Rust's memory safety is also statically determinable due to its type-based ownership model and borrow checker, which operate at compile-time. This allows Rust programs to execute efficiently without an underlying runtime or a garbage collector, as memory safety and resource management operations (like cleanup routines that invoke destructors) are deterministically inserted by the compiler when a resource is released by its owner (e.g., falls out of scope).

Another aspect of Rust that contributes to its high performance is its focus on zero-cost abstractions, in which libraries expose higher-level, easier-to-use programming interfaces that still compile down to the equivalent of manually-optimized machine code.

Some concrete examples of great Rust features include:

Static type and memory safety enable Rust to catch many classes of bugs at compile-time, which reduces developer frustration, minimizes debugging difficulties, and provides a higher degree of confidence that a program will run correctly and reliably after deployment. Early error detection combined with Rust's excellent documentation, tutorial books, IDE language server plugins like rust-analyzer, detailed compiler error messages enable developers to be much more productive than other languages.

In addition, Rust's safety model can restrict possible root causes of bugs to unsafe code regions, though not for basic logic bugs. Rust is also amenable to formal verification, both in terms of verifying soundness of unsafe code blocks, correctness of unsafe-safe code boundaries, and correctness of type-carried invariants.

While most Rust features and benefits apply to all platforms and execution environments, certain ones are key for mobile platforms. High performance is particularly important in interactive systems like mobile devices, in order to provide a smooth, responsive UI/UX without jank. Similarly, efficient operation is crucial to extend usable life in battery-powered devices and reduce thermal output in body-proximal hardware without active cooling.

Rust's core ecosystem is excellent

Finally, Rust comes with an excellent suite of features and tooling beyond the core language itself. First, Rust's primary compiler rustc uses LLVM as a backend, which affords it two main benefits:

  1. Rust can leverage LLVM's existing support for a wide set of target triples, including many architectures, target OS platforms, and library environments.
    • Most importantly, the compiler supports cross-compiling by default, making it super easy to build code for a different target than your host. To state the obvious, quick and easy cross-compilation is a key prerequisite for a multi-platform app dev framework.
  2. Despite being a relatively young language, Rust can immediately realize highly-optimized code generation thanks to decades of effort poured into LLVM by compiler experts.

Second, Rust's package manager cargo vastly simplifies discovering, specifying, and correctly building dependencies for your project. It integrates tightly with crates.io, a registry of crates (projects) from the Rust community that makes it trivial to publish, distribute, and depend on other open-source crates. Furthermore, cargo makes it easy to specify dependencies as an exact version or a range of versions of a given crates by utilizing SemVer semantic versioning. This design avoids the versioning hell that frequently plagues other languages and package manager, and there are even community-provided tools to ensure semver compliance when releasing new versions of a crate.

Third, the set of packages available in the Rust world is far larger in both number and scope than that of Flutter, the leading cross-platform app dev framework. Rust's crates.io registry includes around 130,000 packages, while Flutter's registry of Dart packages pub.dev is only about one quarter of that. More importantly, the breadth of domains covered by the Rust crate ecosystem is massive, including topics like compression, cryptography, finance, game engines, mathematics, multimedia, parsing, scientific computing, text editing/processing, and more. This allows a Rust app developer to immediately and easily bring in a ready-to-use set of dependencies across a wide berth of app topics, which more likely covers unique, niche app use cases. In comparison, the Dart package registry is primarily concerned with basic app topics, e.g., UI widgets and UX behavior.

Rust does have some relevant shortcomings

While Rust offers an excellent experience for systems development, it does have some disadvantages from a UI standpoint, many of which represent obstacles for its usage in application development.

1. Lack of inheritance

First, Rust doesn't support true type-level inheritance, only interface-level inheritance (at the trait level). This makes it difficult to realize the object-oriented programming design patterns to which UI developers are typically accustomed, e.g., a base displayable/widget class from which other subclasses can inherit and override functionality as necessary.

2. Representing shared mutable state is hard

Second, it can be difficult to implement access to shared mutable state in Rust. The ownership and borrow-checking semantics are not yet sophisticated enough to support all use cases and programming patterns typically found in UI development and state management frameworks, e.g., updating a state from a background thread that is then read by the main UI thread to re-display it on a widget. This is because Rust strictly enforces an "aliasing XOR mutability" rule for memory safety, meaning that there cannot be multiple simultaneous mutable aliases to a piece of shared state — only one mutable alias or multiple immutable aliases, but not both.

For example, a global static variable can be freely aliased multiple times, but only accessed mutably through unsafe code, which is undesirable, or via a wrapper type that offers interior mutability with some sort of mutual exclusion mechanism, e.g., a Mutex. Even for states shared across different parts of a single thread, e.g., among async tasks, mutability still must be separated from aliasing, which is typically realized via the RefCell wrapper type (a single-threaded Mutex equivalent). Also, as Rust lacks runtime garbage collection, the de facto alternative is to wrap states with reference-counter pointer type, e.g., Rc or Arc, for easy sharing of states whose lifetimes are only determinable at runtime. The result of these rules and conventions is that code becomes less ergonomic and readable, as it must be littered with Arc<Mutex<State>> or Rc<RefCell<State>> patterns rather than just direct access to State.

3. Slow compilation

Third, the Rust compiler has a reputation for being "slow" in that compile times for large Rust projects can be high, at least compared to other languages. This is primarily due to the need for and the ability of Rust to perform more safety checks at compile time than other languages. The problem with slow compilation is that it lengthens the iterative development cycle, making it more tedious to test a quick change and reducing the overall productivity of app developers. The good news is that compilation times are continuously improving thanks to the efforts of Nick Nethercote and others, but this still may pose a problem to Rust frameworks that wish to incorporate many features into a project build.

4. Steep learning curve

Finally, Rust is known to have a steep learning curve, as the aforementioned rules for memory safety often present a barrier to newcomers, colloquially referred to as "fighting the borrow checker". While systems programmers and low-level programmers may already possess a deeper understanding of memory models and memory safety restrictions, frontend developers are less likely to be intimately familiar with these topics, as they are accustomed to working in higher-level languages that abstract away and handle memory management implicitly. A survey from Very Good Ventures found that frontend developers needed around 3.5 months of retraining to switch from a platform-native framework to the cross-platform Flutter framework before they could be equally productive. It is possible that switching frontend developers to a Rust-based multi-platform framework like Robius could impose an even steeper barrier to entry. As framework designers, we must be careful to strike a balance between explicitness, expressiveness, and simplicity in order to not get in the way of app developers' intentions and productivity.

Project Goals

The Robius project has set forth the following goals:

  • Establish a Rust app dev community and drive ecosystem development
  • Create reference design of full app + system stack
  • Develop flagship apps and simpler demo apps as proofs of concept
  • (Future) Give feedback to Rust's lang, libs, and cargo teams

The following sections provide more details on each of these goals. All in all, we hope that Robius will attract front-end developers to the world of Rust and encourage existing Rust experts to broaden their horizons and explore the application development domain as well.

Community and ecosystem

Our goal is to establish the Robius project as a collective, communal space for all things related to multi-platform app development in Rust. As this community doesn't yet exist, we are building it from the ground up. Thus far, we have the project-robius GitHub organization that aims to serve as a starting point, but once the vision has had more time to incubate and grow, we will create a working group related to app dev in Rust. We also intend to host and maintain an "are we app yet" website that tracks the status and progress of our Rust app dev work in a public and easy-to-follow format.

As part of building this community, we aim to collect and maintain a set of crates related to app dev that are highly functional and actively supported. In short, this amounts to identifying key projects in the space and inviting their authors, maintainers, and contributors to participate in the larger Robius vision. Thus far, we have welcomed two major UI frameworks, Makepad and Dioxus, and a platform abstraction framework, Osiris, into the community. We aim to foster collaboration between these founding projects in order to enable each project to jointly benefit from contributions and improvements to other projects.

If you are interested in adding your project or suggesting another project to add to Robius, please don't hesitate to reach out to us. We're always looking for more projects to include in the Robius ecosystem!

Another major goal of the Robius vision is to drive the non-code components of the ecosystem forward, i.e., documentation. This whitepaper is one example, but we also strive to publish and support holistic (cross-project) docs and detailed tutorials, as well as provide a platform for blogs and discussion forums related to Rust app development.

Reference design for full-system stack

The central goal of the Robius project is to deliver a complete working solution for multi-platform app development, in which all components in both the application and the systems stack are written in Rust.

We call this a "reference design" because it embodies only one incarnation of how the ecosystem of loosely-coupled components can be assembled into a fully-working stack. The intent is for the reference design to act as a "blessed" systems stack that new developers can use to quickly and easily start building apps, i.e., a pre-integrated combination of components that are known to work well together. At the same time, we wish to allow expert developers to tailor the stack to meet their more complex needs, e.g., by selecting or building different components to replace individual pieces of the stack.

The goal of the Robius reference design is to support all major platforms and OS environments, with mobile and desktop being the priority targets:

  • Mobile: Android, iOS
  • Desktop: Linux, macOS, Windows
  • Other: OpenHarmony
  • Web (plus WebAssembly)

The following diagram shows the proposed architecture of this reference design, which is described in greater detail in the system architecture section of this whitepaper.

Robius Reference Design

Flagship apps and examples

In addition to the systems stack reference design, our other important deliverable goal is a series of open-source applications, in two broad categories:

  1. Flagships apps
    • Full set of features with robust UI & UX
    • Intended for actual usage/dogfooding
    • Commercial quality; publishable to app stores
  2. Example/demo apps
    • Simple structure intended to demonstrate a single feature
    • Minimal, incomplete UI & UX
    • Missing features represented by mock stub implementations, e.g., simulated server/database backends
    • Can be used for automated testing/benchmarking

We will strive for both flagship and example applications to be designed clearly, well-structured, thoroughly commented, and accompanied by documentation like a tutorial-style walkthrough. The overarching goal is for these apps to be easily forked and modified by new developers, such that they can use these apps as templates for a quick start.

Our primary flagship application will be a fully-featured Matrix chat client called Robrix (Robius + Matrix). The rationale behind this decision is manyfold:

  • The Matrix protocol, like Robius, is open-source, free, and decentralized.
  • A chat client's features depend on a wide set of functionality from the underlying system, which will help steer and prioritize development of Osiris and its integration with UI toolkits like Makepad.
  • A chat client consists of multiple different UI views, which will drive development of a robust widget library and an expanded set of UI toolkit features.
  • It requires complex app/business logic like user authentication and account management, sending, receiving, and synchronizing messages, handling persistent state (caching, settings), delivering notifications, accessing local storage, etc.
  • Demanding features can be optionally added to stress test our system abstractions, e.g., audio/video chat, multimedia capture and playback, image display, etc.
  • Several Matrix chat clients already exist, which comprise a good basis for comparison between the Robius framework and the frameworks used to implement those clients. We can evaluate both the qualitative development experience and resulting application behavior of Robrix as well as its quantitative performance, relative to that of the other chat clients.

The below diagram shows a tentative overview of the architecture of Robrix, including the components needed to realize its full set of features. We note that while this a multi-year endeavor, we hope to have a basic MVP available by mid-2024. We invite anyone to contribute or test out Robrix via our GitHub repo here.

Robrix Matrix Chat Client Proposed Architecture

For the example applications, some initial ideas are listed below, several of which are already in progress.

  • Simple to-do or notes app
  • Social media news feed, e.g., Twitter, Facebook
  • Social video sharing apps, e.g., TikTok, YouTube shorts
  • Shopping/e-store app, e.g., Taobao, eBay, Amazon, Etsy
  • Basic drawing/sketch app, e.g., paint

In the long term, our ultimate goal is drive the development of Robius into a robust, stable ecosystem that inherently demonstrates what is possible with Rust. We can then leverage the positive experiences of creating apps atop Robius to evangelize for the usage of Rust in the app dev world, especially in business scenarios.

Our objective is to show that businesses stand to gain the following benefits from switching to Robius, many of which correspond to the motivating factors behind the Robius vision itself.

  • Usage of a modern framework and highly-desired language tends to attract top developers and improve team culture and morale.
  • A multi-platform framework reduces headcount overhead, as developers only need to know Rust and the framework, not the intimate details of each platform.
  • Customers get to enjoy a more consistent UI and UX across disparate platforms.
  • Maintainability is easier, enabling more rapid and consistent bug fixes at lower cost, which also leads to higher customer satisfaction and engagement.
  • Smooth integration with the rest of Rust's huge ecosystem of crates allows developers to lean on the significant shoulders of open-source projects, especially beyond UI elements.
  • An open-source, distributed, and decentralized project structure gives business leaders confidence about the staying power and independent directions of the framework.
  • Last but not least, developers get to use Rust!

Technical Requirements

This section expands on the requirements inherent to the success of the Robius vision and our initial strategies for how to meet them. These requirements can also be seen as additional project goals, but more technical in nature than those in the previous chapter.

As shown in the following Venn diagram, we have split the set of target requirements into two broad categories: those related to runtime, and those related to build time or developer tooling. Certain requirements touch on both runtime and build-time system aspects. The following sections discuss each requirement in more detail.

Technical Requirements

Efficient, performant execution

The first and most important requirement falls under the runtime behavior category: high performance, efficient execution. While this can be a bit of a nebulous goal, our general target is to be at least as performant and efficient as platform-native applications. Specifically, the minimum performance requirement for Robius across all platforms is for standard application animations to be able to achieve a consistent framerate of 60 frames per second (FPS), which is the native framerate of most legacy device screens. Efficiency-wise, apps built using Robius must not have a larger memory footprint nor consume more power than an equivalent app written using the platform-native SDK.

While 60FPS is the bare minimum, modern mobile devices typically support higher framerates, such as flagship smartphone screens and virtual/augmented reality head-mounted displays that operate at 90Hz or 120Hz; high-end desktops and laptops frequently have monitors capable of even higher framerates up to 240Hz. Therefore, the higher-end performance framerate that Robius will target is 120Hz, in order to fully leverage modern mobile hardware.

As pictured below, the central requirement of high efficiency and performance leads to follow-up requirements (in green) of (1) high responsiveness and (2) low energy consumption. It also stems from prerequisite requirements (in orange) of (1) an efficient, performant implementation language (i.e., Rust), (2) the avoidance of slow communication mechanisms with the underlying platform, and (3) the availability of low-overhead, accurate profiling mechanisms across the system stack.

We discuss these individual post- and pre-requirements in more detail below.

Efficient, Performant Execution

Pre-requirements: what leads to efficiency/performance?

Three main factors will contribute to the ability of Robius to meet these requirements:

  1. Using an efficient, performant language.
  2. Using efficient mechanisms to communicate with the underlying platform.
  3. Supporting a low-overhead mechanisms to profile the application, inclusive of the entire Robius stack.

Choice of Language

The first pre-requirement is already met by virtue of our project charter to use Rust. As described in a previous motivation subsection, Rust is generally quite efficient because it does not have an underlying runtime or garbage collection (as with C and assembly), so nothing is required to run between the generated code and the machine code. Another aspect that further contributes to the ability to achieve high performance with Rust is its aggressive reduction of runtime overhead via zero-cost abstractions and compile-time operations, e.g., type and memory safety analysis along with a powerful (and ever improving) engine for const evaluation. Rust also makes concurrency easy to realize with confidence due to built-in traits for defining thread safety (Send and Sync), which reduces the complexity of and barrier to entry for improving performance by leveraging multi-core hardware.

Communication mechanisms

The second pre-requirement — using low-overhead mechanisms to communicate with the underlying platform — is one that both Osiris and the UI toolkit(s) will need to address The most informative perspective from which we can look at this requirement is to examine what not to do, i.e., learn from the disadvantages of other cross-framework "bridge" approaches. Bridge projects support interaction between app dev frameworks and other programming languages, both for native (e.g., Jetpack Compose, SwiftUI) and cross-platform frameworks (e.g., Flutter, React Native). However, they typically use (de)serialization to send data between the framework and other language, which sometimes also operate in different processes, requiring inter-process communication (IPC). Both serialization, deserialization, and IPC mechanisms all contribute to high overhead, which in turn reduces efficiency (longer battery life and less thermal output), lowers responsiveness by imposing additional latency into the app's main event loop, and harms overall performance.

For Osiris, our guiding philosophy is to first expose direct unfettered access to native platform APIs with no abstraction layers atop them, i.e., implement platform functions and types via FFI/extern definitions. Only after native features are fully exported via direct FFI will Osiris begin to develop low- to zero-cost abstractions atop these FFIs. Thus, by using direct in-process foreign function calls and FFI-compatible type definitions instead of expensive IPC or serialization, we should be able to avoid platform communication overhead and meet this requirement for all non-UI componentry. Note that Osiris also follows this philosophy for its build tooling.

Similarly, the Makepad UI toolkit has always been strongly against unnecessary or overly-deep abstraction layers, instead preferring to directly use platform-native functionality via in-house custom crates, which not only reduce runtime overhead to a bare minimum but also keep compile times very short. Other UI toolkits may choose different points in the design space with varying tradeoffs between communication overhead and level of abstraction, but they must aim to keep these costs minimal by avoiding serialization and other expensive data marshalling operations wherever possible.

Profiling mechanisms

The third pre-requirement is to enable developers to easily profile the performance of any part of their application as well as any component within the Robius system stack. This ties into the other requirements because profiling exists to help developers resolve performance problems by determining where bottlenecks and slowdowns exist in their code.

To meet this requirement, we aim to leverage standard sampling-based performance analysis and tools to generate accessible overviews, e.g., perf and flame graphs. We also intend to implement detailed tracing mechanisms, à la Linux's function tracing (ftrace) or the Rust tracing crate, in order to allow developers to easily collect diagnostic data by turning on instrumentation events, ideally at module-level and/or criticality-level granularity. In addition, we will provide detailed documentation on how to set up and use existing debugging tools with the Robius framework, while ensuring that build toolchains are capable of emitting necessary debug info that is accurate for all integrated Rust components. Finally, we plan to openly collaborate with the Oniro project on a joint endeavor to implement tracing support on new platforms like OpenHarmony.

As Robius continues to develop, we will expand upon our strategies to meet this requirement.

Post-requirements: what stems from efficiency/performance?

The primary benefit of high performance and efficiency is responsiveness, while the secondary benefit is energy efficiency.

Responsiveness

Responsiveness is typically measured in terms of end-to-end latency: the elapsed time from when an input occurs to when the resulting visual change from that input is first displayed. For most modern devices, this latency is on the order of tens to hundreds of milliseconds, i.e., 20ms – 150ms. Low responsiveness (high input-to-display latency) is most noticeable and detrimental to the user experience on touchscreen devices, which are commonly found on mobile platforms and occasionally on laptops. To understand why, check out this video from Microsoft Research's classic touch input latency experiment.

As with the other related requirements, our initial target is that an app using Robius must not exceed the latency of an equivalent app built for the native platform. It is difficult to set a more precise target for input-to-display latency, as many impactful factors lie beyond our framework's control: the native platform's software latency, the underlying hardware (especially graphics accelerators), an application's implementation decisions, and so on.

One likely candidate to use as a basis for comparison is the responsiveness of scrolling a webpage in a platform-native webview, as web scrolling is a very common action that general users are familiar with and can understand and visualize. This series of experiments shows latency measurements for web scrolling on a variety of mobile and desktop platforms, which could serve as a baseline starting point for this requirement; however, many of those figures are for older devices circa 2017. In the future, we may be able to set a more specific responsiveness goal for certain combinations of specific application behavior on specific platforms.

All of the aforementioned tactics to achieve high performance also apply to meeting latency requirements. In addition, we anticipate experimenting with ylong's support for priority-based async task scheduling, in which we can assign interactive tasks a higher priority than background tasks or those that do not directly affect the UX and UI responsiveness. To the best of our knowledge, no other async frameworks offer a customizable notion of per-task priority. This may bring a significant improvement compared to tokio, the de facto async framework of choice, especially for applications that are heavily I/O-bound.

Energy efficiency

The secondary target benefit is energy efficiency, which is particularly important for battery-powered devices, e.g., mobile, embedded, and extended reality headsets. This is reflected in nearly every market survey, in which consumers rank battery life as the most important characteristic when buying a smartphone.

As with the prior requirements, energy efficiency is affected by many disparate factors, so we again return to the conservative metric of Robius apps not being less efficient than equivalent apps written for the native platform. Comparatively speaking, this requirement should be easier to meet due to the nature of Rust-generated machine code lacking overhead of an underlying runtime, VM, or garbage collector. Clearly, the UI layer also has a significant impact on energy consumption, which is why our UI toolkits emphasize efficient operation in their design choices. For example, Dioxus uses intelligent diffing to reduce the volume of updates required to its virtual DOM, which prevents unnecessary re-rendering operations; Makepad bypasses the underlying webview component entirely, as they are known to be relatively heavyweight, in favor of directly accessing the native graphics layer to perform rendering operations.

A future line of work will involve supporting energy-aware and topology-aware scheduling of tasks/threads across heterogeneous CPU cores, which is common on mobile devices, e.g., ARM's dynamIQ system (previously big.LITTLE). This will enable our concurrent/parallel execution layer to more intelligently utilize the hardware's available power budget by considering it jointly alongside the CPU topology and capability of each core.

Fast application startup

The second requirement in the runtime behavior category is fast startup for applications, i.e., a short app launch time. Decades of surveys have revealed the importance of this metric, as it affects the overall responsive feel of the system and can negatively impact consumer opinion of a company. For example, 70% of mobile app users will abandon an app if it takes too long to load, and between 49% to 59% of users expect apps to start in under 2 seconds. Similarly, studies of webpage load times found that an extra 0.5 seconds in Google page generation results in 20% less traffic, every 100ms of additional latency reduces Amazon's sales by 1%, and that ultimately 53% of visitors will leave a website if it takes over 3 seconds to load.

Our target is a 2-second cold start, which means that an application being launched for the first time since system boot must display its UI and be interactive within 2 seconds. The time to initial display (TTID) should be far less, in that the application must display at least something within 500ms. We derive these requirements from guidelines published by mobile app publishers (iOS and Android), who recommend cold starts within 3-5 seconds, warm starts within 2 seconds, hot starts within 1.5 seconds, and TTIDs within 250-500ms. We tend to the lower side of the cold start metric, because fast cold start times benefit not only users but also developers, as it helps to shorten the iterative build-run-test development cycle.

Notably, some of these factors are beyond the control of Robius. For example, on Android, a cold start requires the following procedures: process creation, class loading, application initialization, activity creation, layout inflation, and rendering. The first two are completely under the control of the platform, while the other four are at least partially under the domain of Robius components. Similar procedures are undertaken on iOS and desktop platforms.

That being said, design and implementation choices within Robius can affect all stages of application startup. The primary directive of writing everything in Rust removes the concern about inconsistent performance due to garbage collection or just-in-time compilation jank at startup, because Rust is a fully ahead-of-time compiled language. However, Rust binary sizes are notoriously larger than other languages, which may increase loading and dynamic linking time when creating the application process. In general, Robius will follow other canonical best practices for reducing startup latency: maximize lazy initialization, avoid synchronous operations, move work off of the main UI thread, etc.

Quick compilation

The third requirement for quick compilation lies solely on the side of build tooling. Compile time has an even more significant impact on the developer experience and iteration cycle length than the previous requirement of fast app startup.

It is difficult to set an absolute value for this metric, as compilation is heavily affected by the nature of the application content. However, we can establish a target that is more relative: compiling the Robius frameworks shall not constitute an unbearable addition to the overall compilation time of the application. While this is unrealistic for simple applications, we can revise this into the following:

Incremental compilation must be fast enough to be interactive.

This means that a quick code change should be compilable within a few seconds, such that the developer does not suffer loss of mental focus or context while waiting for compilation to complete.

One major challenge is the pervasive notion across online communities that the Rust compiler is slow. Although this is a contentious issue, it is true that rustc must do more compile-time actions than other languages with fewer static safety guarantees. While the compiler team has taken significant action to improve compiler performance, Robius must be careful to explicitly subvert this negative expectation.

Robius makes several design choices to combat high compilation times. First and foremost, both the Makepad and Dioxus UI toolkits support hot reloading, in which an application can be live updated without restarting it. This enables developers to instantly observe changes to an application's UI without needing to re-compile at all. Makepad also provides a minimal set of in-house dependencies for very fast compilation, which helps speed up iterative rebuilding of application components that cannot yet be hot-reloaded. Another contributing aspect is the desired project structure of many small crates, which enables Robius to benefit from parallelization of distinct compilation units.

Consistency across platforms

The fourth requirement of cross-platform consistently is relevant to both runtime and build-time behavior, though is primarily focused on the latter. The key part of this requirement is that developers should never be required to touch platform-specific code in their application. However, they should be able to access platform-specific functionality with relative ease, if desired or needed. Though challenging, Robius can meet this requirement through careful design of abstractions that easily reveal lower-level features, e.g., downcasting Rust traits to concrete platform-specific types.

The runtime-focused aspect of this requirement is that platform-agnostic abstractions should impose no overhead (or as little as possible) compared to using the underlying platform-native code directly. Although this is technically more of a design-time requirement, the implementation choices therein will ultimately affect runtime behavior, namely execution efficiency. To this end, Rust libraries typically strive to offer zero-cost abstractions, and Robius components shall do the same in this regard.

Flexibility to incorporate other components

The fifth requirement for flexible componentization is primarily a build tooling concern. This encompasses two sub-requirements at varying levels of importance:

  1. Developers must be able to easily incorporate arbitrary third-party components (crates) into the system stack.
  2. (Future) Developers should be able to select between one of many choices for various modularization levels, e.g., pick from multiple UI toolkits when setting up an application project environment.
    • Ideally, a default component could be replaced with a drop-in compatible third-party version of that component1.

Robius's build infrastructure must be able to support flexible, modular build manifests. In addition, the constituent individual projects shall not be tightly coupled to one another, but rather loosely coupled via abstract layers that enable compatibility across different projects that inhabit the same role or responsibility, e.g., UI toolkits, platform abstractions, concurrency runtimes, etc. Currently, we are working with members of the Cargo team to add any required missing features to Cargo, as well as experimenting with higher-level build systems that sit atop and invoke Cargo as needed. The Osiris project is also developing its own Cargo-compatible build tool for expressing complex build procedures related to packaging applications into publishable units.

1

This is a distant goal of Robius, but initial versions will likely not support arbitrary drop-in replacement components until Robius is more stable and cross-component boundary interfaces have been clearly established.

Compliance with platform policies

The sixth and final requirement is for real-world applications developed using Robius to be fully compliant with platform-specific policies. This concerns both runtime behavior and decisions made at build time, with the ultimate purpose of ensuring that Robius apps can be published to actual app stores, e.g., Google Play Store (Android), Apple's App Store (iOS, macOS), Microsoft Store (Windows), Flatpak's Flathub (Linux), and more.

Attempting to publish applications that do not comply with these policies results in errors, such as these returned by build tools on iOS:

  • Error: the app references non-public symbols in <path>. If method names in your source code match the private Apple APIs listed above, altering your method names will help prevent this app from being flagged in future submissions. In addition, note that one or more of the above APIs may be located in a static library that was included with your app. If so, they must be removed.

  • Error: Invalid Bundle Structure - The binary file <file> is not permitted. Your app can't contain standalone executables or libraries.

  • Note: applications must be packaged and submitted using technologies provided in Xcode; no third-party installers allowed. They must also be self-contained, single app installation bundles and cannot install code or resources in shared locations.

The corresponding design-time requirement is that Robius must not depend on private platform APIs to realize higher-level abstractions. Technically, we can leverage such hidden functionality for "hacky" non-compliant solutions that speed up dev builds, as long as they can be transparently replaced with the official APIs for publishable release builds. For example, Makepad provides custom, stripped-down versions of Android and iOS SDK modules, which shrinks the size and scope of these dependencies, significantly reducing compile times. However, publishing an app built against these dependencies may not be compatible with all platform guidelines.

To this end, Osiris build infrastructure ensures that platform requirements are met by always using the native platform toolchain to generate the final package. This prevents accidental violations of packaging requirements, e.g., including third-party components that are not allowed in the app package. An additional advantage of this approach is easy integration with other platform-specific tools, such as linters, package checking systems, static analyzers, performance profiling tools, and more.

The corresponding runtime requirement is that Robius must not attempt to download or run third-party code not originally included in the app package, which is mostly a concern on mobile platforms. Loading said foreign code is expressly forbidden on iOS and implicitly disallowed on Android. Similarly, just-in-time (JIT) compilation of code is also prohibited on iOS, while Android's ART runtime has deprecated JIT in favor of ahead-of-time (AOT) compilation. Android also therefore no longer supports loading class files from native code. These restrictions render certain potential mechanisms for cross-platform abstractions impossible, forcing us to adopt other solutions that comply with these rules.