Extern "Rust" types that are nonlocal
The following worked prior to 1.0:
type DataSourcePrivate = Rc<DataSource>;
#[cxx::bridge(namespace = "mcrtlib::ffi")]
mod types {
pub struct DataSource {
pub inner: Box<DataSourcePrivate>,
}
extern "Rust" {
type DataSourcePrivate;
// ...
}
}
However, now I just get a
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> library/src/ffi.rs:85:9
|
85 | type DataSourcePrivate;
| ^^^^^-----------------
| | |
| | `Rc` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
Is it possible to make this work again or should I wrap my Rc in a tuple struct?
This is restricted to a local type for now because if multiple bridges reused the same type as an extern Rust type in the same namespace, they ended up colliding on symbols. I expect to relax the restriction again but it needs some design work and I wanted to be slightly more conservative for 1.0.
Ahh, fair enough, I figured it was something like that. Then I'll just wrap it in a tuple struct.
Is there an approximate timeframe for relaxing it? I am only asking because if it's likely to work in 2-3 weeks I will keep adding bindings for new types/modules and wait to switch to 1.0+, otherwise it makes sense to switch immediately.
I had a chance to think about this today and I think here's what we'll do:
-
If an extern "Rust" type does not appear in Box and does not appear in Vec and is not the receiver of any extern "C++" methods, then there are absolutely no restrictions. In practice this means Rust types to which C++ only obtains references
&T/&mut T.#[cxx::bridge] mod ffi { extern "Rust" { type MyType; // ok to be from different crate type Receiver; // must be defined by current crate due to method } unsafe extern "C++" { fn f(x: &MyType, y: &mut MyType); fn g(self: &Receiver); } } -
If an extern "Rust" type does have any of the above, by default we'll require that it is locally defined by the current crate. Additionally, if multiple FFI bridges inside the same crate refer to the same extern "Rust" type within the same C++ namespace, then by default only one of the bridges will be able to use Box of the type and only one of the bridges (not necessarily the same) will be able to use Vec of the type.
#[cxx::bridge] mod ffi1 { extern "Rust" { type MyType; fn f() -> Box<MyType>; } } #[cxx::bridge] mod ffi2 { extern "Rust" { type MyType; fn g() -> Vec<MyType>; } } -
If someone needs the same Rust type used as an extern "Rust" type in the same C++ namespace, in different bridges, and they need both bridges to use the type in Box or Vec, then instead of saying
extern "Rust" { type MyType; }they will need to use an ordinaryusestatement in the bridge (as made possible by #353):use crate::path::to::MyType;.This makes Box and Vec related codegen on the C++ side opt in rather than opt out as is the default for extern "Rust" types. Instead of getting Vec-related codegen just because your bridge used a Vec, you'd need to requested it as described in https://cxx.rs/extern-c++.html#explicit-shim-trait-impls, if some other bridge doesn't already provide that codegen elsewhere.
#[cxx::bridge] mod ffi1 { extern "Rust" { type First; type Second; fn f() -> Box<First>; fn g() -> Vec<Second>; // results in C++ receiving an instantiation of Vec<Second> } } #[cxx::bridge] mod ffi2 { use super::{First, Second}; extern "Rust" { fn h() -> Vec<First>; fn i() -> Vec<Second>; } impl Vec<First> {} // needed because C++ doesn't already know about Vec<First>, and `use` doesn't automatically create it } -
Lastly, if we're dealing with types that are from a different crate, your explicit
implwould be required to be anunsafe impl. It's on the programmer to guarantee that multiple crates aren't instantiating the same C++ Box or Vec for the same type. (It's technically undefined behavior. In practice nothing bad would actually happen since all duplicated symbols would be functionally identical, I think the worst is you'd get linker errors.)A completely reasonable situation for which the programmer can make this guarantee is something like:
struct PrivateType {...} // private to this crate type MyType = futures::channel::oneshot::Sender<PrivateType>; #[cxx::bridge] mod ffi { use super::MyType; extern "Rust" { fn f() -> Box<MyType>; } unsafe impl Box<MyType> {} // no other crate could possibly contain this instantiation because they don't have PrivateType }
What is the solution to this today? Seems like using use statement is no longer supported and when I simply use type X; in two different bridges, I get a error[E0119]: conflicting implementations of trait 'RustType' for type 'X'.