Adoption of Rust over time in existing game codebases
This is very similar to #29 and there are a lot of great comments that are relevant to this there. However, I think #29 is along the lines of "how can an end-user of an engine use rust with existing engines" and I'd like to explore "how can teams maintaining existing game codebases migrate to rust over time"
In industry, established studios are generally using a large existing codebase. Any new language or technology often gets introduced slowly over time, and it's adopted incrementally. (Along the lines of this tweet: https://twitter.com/AndreaPessino/status/1021534287202402305)
I think more demonstrations and proof-of-concepts that Rust can link against and reuse C code would be beneficial to the community. I've personally been using the imgui and gpc crates to call out to a couple libraries in C. (In some ways cargo makes it easier to pull existing C code into a rust project than it would be in most C/C++ environments.)
I think it would also be helpful to consider the use case of linking Rust code into an existing codebase. This could be C++ linking against a rust binary, or something like C# pinvoke into a DLL.
In short, adopting Rust doesn't require throwing out existing code, but I'm not sure this has been well-messaged outside the Rust community.
I have a few questions:
- Is it in scope for this WG to consider the path that existing studios would take to adopt Rust? If so: ** Are there any improvements that can be made to the Rust ecosystem to improve this interop story? ** Is the fact that there is an interop story something we'd like to call attention to in some way (particularly outside the Rust community)? If so, how can we do that? ** Do we know of any groups that have gone far down this path? It would be great to have their post-mortem perspective on it to know how it went, and so that we could explore opportunities to ease that path for others in the future.
Writing a Rust lib that exposes and/or consumes a C API is extremely easy and only exactly as terrible as any other time a C API is involved. You can even hot reload dynamic libs at runtime and other C tricks just as well in Rust.
This biggest problem is that for most people, if you'd use a C API library at all you'll just use a normal, actual C library that works perfectly with all your existing C tooling.
Everyone else assumes you're writing in C as well. For example, if you have performance problems, Intel has a profile tool you can build into your program by including a header file and you call start and stop macros and it spits out profile data. You can't do that so easily in Rust because it's not that big yet.
I saw this today, they have an interesting approach to generating bindings to PhysX.
https://github.com/EmbarkStudios/physx-rs
I think it’s a useful reference/demo of using a non-trivial C++ lib from Rust.
Yes, also https://m.youtube.com/watch?v=t99L3JHhLc0 offers great patterns that can be applied in the domain of interacting to/exposing existing C libs.
Some more resources I've found:
- It's worth mentioning explicitly, Firefox is an example of C++/Rust Interop at-scale. Some information here: https://wiki.mozilla.org/Oxidation
- C++-compatible FFI
- There is prior art in D to support static linking with a subset of C++. There are some details on how it's done and the restrictions here: https://dlang.org/spec/cpp_interface.html.
- This was further discussed here: https://internals.rust-lang.org/t/interfacing-d-to-legacy-c-code-a-summary-of-a-competing-languages-capabilities/1406
- There was a pre-1.0 bug (https://github.com/rust-lang/rust/issues/5853) and an RFC https://github.com/rust-lang/rfcs/issues/602. The RFC remains open, but I haven't seen any indication that it has been worked on
- More discussion here: https://internals.rust-lang.org/t/better-c-interoperability/2650
- There is now support for cross-language LTO: https://github.com/rust-lang/rust/pull/58057
- This seems like it could work for a subset of C++. The original C++ would likely need to be modified to provide an interface that is easier to bind.
- Mechanical generation of C/C++ wrappers
- bindgen supports C and some C++: https://crates.io/crates/bindgen
- SWIG - http://www.swig.org/Doc4.0/SWIGPlus.html#SWIGPlus - I think this can support a larger subset of C++ than bindgen (for better or worse).. in any case I found the documentation helpful
- Mentioned previously, but Embark's PhysX bindings had an interesting approach: https://github.com/EmbarkStudios/physx-rs/tree/master/physx-sys
- Transpiling C++ to Rust
- c2rust: https://www.youtube.com/watch?v=WEsR0Vv7jhg
- Compiling C++ within Rust (Not convinced that this is a practical approach, but would love to be proven wrong!) - https://docs.rs/cpp/0.5.3/cpp/
I was just thinking today, if someone wants to research into this further and get real-world experience, one approach could be to try porting some code in an existing game engine. For example, pick some isolated module in Unreal Engine that seems relatively easier to port and give it a try. This could be something gameplay level, but it could also be something non-shipping like the something in the asset pipeline or project build pipeline.