Open fw6 opened 1 month ago
Hi, thanks for checking out UniFFI!
UniFFI imposes many restrictions on your interface which means many Rust patterns do not really work. In particular, returning &mut Self
isn't supported - objects like this always need to be wrapped in an Arc<>
so they can be correctly shared with foreign bindings. &mut self
as an argument isn't supported either as the interface needs to be "Send + Sync". You typically get mutable references by using mutexes, but this tends to mean the builder pattern isn't going to work in the same way.
This is described in the manual - in the first instance you are probably best working with our examples and test fixtures to see what is and isn't supported.
I've been wanting to support [Self=ByArcUnwrapOrClone]
¹ that adds a call to Arc::unwrap_or_clone
to the scaffolding to enable self
consuming methods, which would help a ton for by-value builders. I guess the same idea could enable &mut
methods via some [Self=ByMakeMut]
attribute, adding a Arc::make_mut
call to the scaffolding code. I didn't end up putting time into this after switching jobs into a position where I no longer use UniFFI professionally.
I think the builder pattern is the perfect motivation for this, so maybe it makes sense to re-open this issue? Or maybe better to open a new one for this proposal. cc @bendk
¹ using UDL syntax here, but a proc-macro attribute would be just as interesting if not more
so maybe it makes sense to re-open this issue?
Sure, let's do that!
Interesting idea. I think if you used unwrap_or_clone
we would always use the clone
side of it, since there's no equivalent on the foreign side to a self-consuming method and we have to assume that the foreign code might have another reference to the object. However, that could be fine. From Rust's POV it will look like a self-consuming method and nowadays you should be able to return a Self
value and have UniFFI automatically wrap it in an arc. From the foreign code's POV, it's a method that returns a new object. The fact that we needed to make a clone and a new arc is slightly inefficient, but most of the time that's probably fine. I think that's how I ended up writing builders anyways in Python.
For similar reasons, I think make_mut
will result in a clone which is probably not what was intended. One thing I've wondered for awhile is if we could have a way for users to opt-out of Rust's safety guarantees, allow &mut self
methods using unsafe code, and make it the caller's responsibility to ensure that those methods aren't called at the same time from different threads. For many languages that's the normal state of affairs and for Python you often write single-threaded apps anyways. It seems reasonable, but I'm also a bit afraid to go down this path.
we have to assume that the foreign code might have another reference to the object
But it has to be freed at some point, is this not solved with the regular Arc
refcount? I'm a bit confused here ^^
make it the caller's responsibility to ensure that those methods aren't called at the same time from different threads
I think this is super risky. Getting unsoundness with unsafe
ly created mutable references does not need to involve multiple threads.
we have to assume that the foreign code might have another reference to the object
But it has to be freed at some point, is this not solved with the regular Arc refcount? I'm a bit confused here ^^l
Yes, so unwrap_or_clone
should work fine. I just think that it's always end up cloning the underlying object.
I’m new to Rust and I’m currently trying to use the
uniffi
library to develop a cross-platform component. However, I’m having trouble finding a suitable way to use the builder pattern to create a struct(crates such as bon、derive_builder).I would appreciate any guidance or examples that could help me better understand how to use the builder pattern in Rust with uniffi.
Blow is my code👇, so many errors occur👆.
user.udl
:Related issue