I haven’t watched the video yet, so the following might be redundant with what’s in there, but I thought a bit recently about how this might best be done.
-
Edit: Just watched some of the video. As far as I can tell, the D solution relies on some separate C++ file doing specializations and manually translating the C++ declarations into D; I don’t like that at all. I think it would be best to use libclang to import C++ headers automatically (including generating code for inlines if necessary), with tweaks; see below.
-
Since C++ templates allow so much crap compared to Rust, but are a relatively small portion of code in practice, I think it would be best to have the user specify up front which template specializations they want to use, have libclang monomorphize them, change angle brackets to underscores (or have the user specify an alias), and have that be the end of it. The only real alternative would be hooking into the compiler during type checking to generate the specializations then, but that sounds awful.
-
Once you get rid of templates, there is no need for direct Rust compiler bindings. The binding generator could just spit out externs corresponding to the mangled versions of all accessible functions and methods, plus inline(always) Rust wrappers to call them with a more desirable syntax. You could do this either with a pure external tool, or a compiler plugin.
-
In the same file as the specializations, the user could also provide binding-like annotations, e.g. while the default Rust translation of a C++ method would presumably be unsafe and take *mut self, one could mark it as safe and taking a borrowed self. Maybe try to guess this based on basic analysis of the implementations (I think you could get pretty far with that, considering the sheer amount of basic getter/setter type stuff in most C++).
-
Overloaded functions would also be good to annotate; functions with the same number of arguments could be exposed to Rust with traits (eww), but not different numbers. This is not Rust’s fault; overloading sucks. It just causes trouble with bindings. One possibility would be to simply append the number of arguments to overloaded functions’ names, OpenGL style, and do the rest with traits; another would be to expect the user to provide saner names using the annotation file; another would be to make every binding take a single tuple argument, and impl traits on that. Defaults are similar, though I really wish Rust had native support for those…
-
Classes would be handled by brute force - every class is a trait, and every concrete class is a struct with explicit impls for its trait and that of its the superclasses.
-
You couldn’t implement a subclass directly in Rust because there would be no way to express a good C++ API directly in Rust, but the binding generator could work in reverse too. Perhaps the annotation file could contain arbitrary C++ code, which would mostly be passed directly to the compiler, but with an added construct like
virtual void foo() override = impl;
which would generate a Rust trait, and always_inline C++ code to call into it. Or something. Would be nice to not do two virtual calls, but I don't think a code generator would be able to easily tell whether Rust actually implemented a function with the right signature...
-
Some stuff would still be ugly. No way around it: idiomatic C++ and Rust look very different. But it would still be miles better than having to manually create C bindings; much easier to have Rust burrow deep into a codebase and slowly take over from inside. ;p
-
Incidentally, I’ve been using Swift a bit recently. swiftc binds to C and Objective-C, not C++, but it’s somewhat comparable - Objective-C code doesn’t really feel “right” from a Swift perspective (no structs, no generics, can sometimes require awkward syntax to deal with enums, out pointers, etc.), but Swift benefits enormously from the compatibility. And it feels fairly magical to be able to just type C declarations/inlines into a .h file and instantly use them from Swift. (While I’ve used rust-bindgen on various occasions, I think I get this feeling with Swift more than Rust, in large part because calls to foreign functions are particularly easy to screw up, and the IDE helps get the types right. Just saying. On the bright side for Rust, when you do screw up, swiftc error messages are awful. ;p)
-
As a fairly simple request, it would be nice to have a way to link LLVM IR files directly into a Rust rlib. In addition to C++, I was also interested in the idea of Swift bindings - I’ll probably never get to that project, but both languages can generate LLVM IR, and bindings would be much improved if Rust could easily do LTO with that IR.