Description
When we started building this tool, the choice of WebIDL for interface definitions was deliberately short-term (ref ADR-0001 and we knew we'd need to revisit it someday. Now feels like a good time to start discussing what the next steps for interface definition might look like.
I'll try to do some scene-setting below, but let's consider this an open discussion thread for anyone interested in this topic to share whatever partially-baked thoughts they may have.
To begin, I want to note that I think WebIDL has worked out surprisingly well for our purposes, given that it was designed for a language ecosystem and set of constraints that are different to what we're doing here. It certainly helped with the goal of getting up and running quickly! But we have observed a few pain-points in practice (this list will be updated as we encounter more):
- It's weird to have to use an empty namespace declaration like
namespace example {}
to give a namespace for the component. - It's weird to have to define top-level functions inside the namespace block like
namespace example { my_function() }
, while other interface members as defined as their own standalone items. - We sometimes need to put Rust-specific annotations in the
.udl
to help out with passing things by owned value vs by reference, which is a distinction that only matters internally to the Rust code. - Poor error reporting for invalid
.udl
files; theweedle
crate seems (quite justifiably!) designed more for working with known-good.webidl
files than for helping its users author new ones. - Poor error reporting when there is a mismatch between the
.udl
file and the Rust code; you basically get a gnarly error message from the generated Rust code, which is hard to act on if you don't have a good mental model of how it's generated. - Our syntax for enums/errors with associated data is awkward and hacky, essentially abusing method-definition syntax.
- The syntax only supports named fields, while unnamed fields are typically more common and idiomatic both in Rust and in the target foreign languages.
- It's possible to add comments in the
.udl
file, but they're not available to the parser so we can't do interesting things with them (such as auto-generating docs for the generated code). - We're struggling to come up with good syntax for importing types from other crates.
There are probably more pain-points, so please feel free to list additional ones below and I'll try to add them to this list.
So what should the next steps be for the "defining the component interface" part of this tool? We've discussed a few main options in the past:
- Continue using WebIDL, investing more in the developer experience by e.g. providing better error reporting.
- Making a custom IDL that maps more closely to the underlying concepts of a UniFFI component interface.
- Getting rid of
.udl
files in favour of something with Rust macros, in the style of wasm-bindgen.
Love or hate any of those suggestions? Please share below! There may also be other approaches that we haven't noticed yet, and I'd love to hear suggestions for those as well.
Whatever solution we come up with, here's a list of the things we know we'll have to factor in to the design:
- A better story for all or most of the pain-points listed above.
- Don't regress any of the features for
.udl
, in particular default arguments. - Workable for both small examples and larger real-world codebases where the logic is split into multiple files.
- Backwards compatibility for existing components that use
.udl
, and a good story for migrating them to the new syntax. - The ability to do additional code generation from the interface without having to add code to UniFFI (e.g. for additional codegen tasks in mozilla-central)
┆Issue is synchronized with this Jira Task
┆Issue Number: UNIFFI-48