Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a doc comparing UniFFI with diplomat #1146
Add a doc comparing UniFFI with diplomat #1146
Changes from all commits
7597def
631dea0
f024f03
a86e949
eb044e2
2d22d03
ee6ee35
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, this explains a lot the design decisions :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think this helps answer my question about about API stability in UniFFI vs Diplomat. I still feel like the
.udl
serves a similar purpose in the UniFFI world, but because of the way the.udl
kind of maps directly onto the underlying Rust code, I can see an argument that changes in a UniFFI component are more likely to change the FFI surface than in Diplomat.They won't change to FFI surface by stealth, because you'll have to update the
.udl
file...but changing the.udl
file is likely to be the simplest way to accommodate a change in the underlying Rust code. In Diplomat you might instead write some adapter code in the FFI module in order to avoid changing the FFI surface, because there's an obvious place to do it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I think we agree that UniFFI's use of the .udl file and Diplomat's decision to restrict where types are exposed do serve the same purpose in that regard. The broader point I'm trying to make though is that application-services would prefer to not have those guards in place - ie, it would probably prefer a world where the UDL file didn't exist and nor did any limitation about where these types could be defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this work in Diplomat, does the FFI allow you to pass it around as a pointer but not construct one for yourself on the foreign-language side?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No - diplomat doesn't have that problem because the struct definition must appear inside the module, so the foreign bindings, which parse that module, do know the struct elements. This is the same as we discovered in #416 - that forcing all type definitions into the single ffi module would technically work, but the impact that would have on how our code is organized made it less appealing than the status quo.
I added a few words here to try and make that clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I think the toy example is the worst-case for highlighting the duplication here because the two duplicate declarations are separated by just 7 lines of text. In a real-world crate I would expect the duplication to not feel quite so bad because the source struct and its redeclaration would be further apart.
(That doesn't help with some of the other contra points raised here though)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good point, but from my POV, that can almost make the duplication feel worse - eg, you get a rust compiler error, and it can be hard to work out whether the UDL needs to change or the rust duplicate of that UDL needs to change - eg, making something optional means adding a
?
to the udl and a correspondingOption<>
to the Rust - it can be difficult to work out which one you screwed up :)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could imagine explicitly telling the macro about the path to the redeclared struct, like:
Then if it wanted to, the code processing this declaration could go find the corresponding
super::myFFIType
and pull out e.g. its docstring.I think this would probably be more trouble than it's worth (we don't want to re-implement vast swathes of Rust's name lookup machinery, for example) but it's interesting to think about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section is some serious food for thought.
For one, as @rfk points out, we're currently duplicating the struct/function definitions in the UDL. The way I see it: both projects have made a similar design decision. @Manishearth describes it as "we do not want changes far away to change the FFI API". I would maybe reword that to "the FFI should be fully defined in one place". For UniFFI, that place is the UDL. For diplomat, it's the ffi module. (Multiple ffi modules complicates this picture a bit, but doesn't fundamentally change things).
However, there is one difference: in some cases you only need code inside the ffi module. This has the potential to reduce duplication. For example, it would be very natural to move our
Store
definitions inside the ffi module. Also, since types defined inside the ffi module are visible to the rest of the rust code, so we could move types likeLogin
,EncrytpedLogin
,CreditCard
,Address
, etc. into the ffi module. I think external data types would still need duplicate definitions, but maybe that's it.One potential issue with this is that it couples the library with the FFI code. But this doesn't seem to be a problem for our components. As @MarkH points out, our Rust APIs exists purely to service the FFIs.
It really makes me wonder about switching from a UDL-based approach to a macro based approach. At the start, maybe each consumer would simply refactor their current UDL file to a macro. This shouldn't be much work, maybe we could even automate it. After that, we have the ability to do to small refactors that eliminate the unneeded duplication.
BTW, this also could solve some documentation issues since a) we can actually see docstrings when using syn and b) if there's only 1 place where a type is defined then it's clear where to put the docstrings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a brief note in a86e949 which doesn't capture all of this comment, but does briefly say why we should consider this more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
diplomat just treats documentation as "yet another backend", which works reasonably well, since the architecture of a diplomat backend is just "here's the type structure, you know what to expect on the FFI layer, do what you want".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By contrast, the only reason that we don't already have documentation as yet-another-backend in UniFFI, is that the off-the-shelf parser that we use for the IDL throws away comments by default :-(