Open bendk opened 1 year ago
Maybe we could add support for ByRef
args in callback interfaces:
RustBuffer
, probably don't need any special treatment.If we could get this to work, then there's still a question of if the semantics are correct. If the Rust code is passing a reference they find copying the data into buffer or cloning the Arc surprising.
a question of if the semantics are correct.
Yeah, I'm a bit torn here. I don't like breaking consumers, but OTOH, I don't think we ever promised this would work, did we? It seems we didn't have test coverage for it otherwise we'd have noticed it before now. I agree that making it look like we are passing by reference just for us to turn it into a copy behind their back isn't ideal and "un-rusty". Is supporting this something we will regret as a foot-gun in a few years?
Maybe we could add an attribute that makes a trait interface Rust-only?
It's hard to anticipate, but if we created more differences between object interfaces and callback interfaces, allowing consumers to opt-in a Rust-only trait interface could be a good solution.
We have code that does this, and don't care about external consumers being able to implement our traits. They're used internally to cement the API and externally they let a consumer specify they need that interface (which our library presents multiple implementers of). If supporting callbacks with ByRef
isn't a semantically good direction, we would want the ability to label the trait so a callback interface isn't generated for it.
yeah, we do need an attribute here.
Maybe we could add support for
ByRef
args in callback interfaces: ... If we could get this to work, then there's still a question of if the semantics are correct. If the Rust code is passing a reference they find copying the data into buffer or cloning the Arc surprising.
Actually I think this is fine and preferable. Rust code calling the trait will not be impacted at all. Foreign code calling in will mean we have a newly created Bar
we pass a reference to, but I don't see how that's surprising to anyone. Am I missing other cases?
What's involved in supporting byrefs there?
I pushed a use case fixture byref-trait
to my branch for this. I was originally trying to add an attribute to disable the automatic callback trait (while wondering why it was added in the first place vs using the existing callback_interface
in places people wanted foreign implementations), but hadn't made it very far. Looking now into what's required to support ByRef in the current auto-callback interface process.
Actually I think this is fine and preferable.
Sorry I didn't note this before, but I was a bit confused here. I'm not sure it does make sense for a callback interface to be able to take references - these are only implemented on the foreign side, so I agree with Ben's comment that it would be odd for a callback interface to take an reference when it must then clone the referent. OTOH though, I guess that given we appear to be moving towards callback interfaces being avoided in favour of traits, I guess I also can't see why it would be particularly bad for that to happen if it helps this scenario. I'll try and have a look at the branch over the weekend.
A complication here is that traits are always lifted as callbacks - trying to round-trip traits ends up with foreign wrappers each time. This is quite unlike interfaces which manage to round-trip the Arc correctly, and isn't ideal :)
It seems like this attribute will need to change the entire impl of the ffi for the trait - to either be a "callback" or an "object" as a "short-term" fix?
Ben and I chatted about this briefly and agreed the comment I wrote above is what we should do in the short term, and longer term we might be able to implement something better for both cases and make the new attribute a no-op or similar.
@skeet70 does the comment above make sense and is this still something you are looking to pursue? Let us know if we can help.
It seems like this attribute will need to change the entire impl of the ffi for the trait - to either be a "callback" or an "object" as a "short-term" fix?
Those words make sense conceptually to me but don't connect to code changes immediately for me yet. It is something I'm still trying to work on but I've had other things take priority at work for the moment. I'll try to come back to it in a few days and see if I can figure out what code changes that statement implies.
Great! I think it will entail that for traits, instead of generating https://github.com/mozilla/uniffi-rs/blob/main/uniffi_macros/src/export/trait_interface.rs#L17 for the scaffolding we instead will generate closer to https://github.com/mozilla/uniffi-rs/blob/main/uniffi_macros/src/object.rs#L9. I'm sure there will be some devil in the detail though.
When Mark and I discussed this, we thought there should be 3 options for your exporting traits:
0.25.x
)This means that the new interface could would be a new feature that users would opt-in to rather than one that we automatically assume for all trait interfaces.
One question is the ergonomics of it all. Maybe we could use the words "with callback interface", meaning you're taking an existing trait interface and adding callback interface functionality to it:
UDL:
[TraitWithCallbackInteface]
interface Foo {
...
};
proc-macros:
#[uniffi::export(with_callback_interface)]
pub trait Foo {
...
}
In
0.25.0
this worked and thebar
argument was treated as a normalByRef
argument.However this currently fails in
main
because we now also generate a callback interface for each trait.ByRef
arguments are not supported there.