[HN Gopher] CXX - Safe interop between Rust and C++
       ___________________________________________________________________
        
       CXX - Safe interop between Rust and C++
        
       Author : dgellow
       Score  : 121 points
       Date   : 2021-03-24 10:27 UTC (1 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | dgellow wrote:
       | Steve Klabnik published a blog post about this library (and some
       | controversy surrounding it) a few months ago, "The CXX Debate":
       | https://steveklabnik.com/writing/the-cxx-debate
        
         | dtolnay wrote:
         | This is outdated and does not apply to the library today. The
         | resolution is discussed at the bottom of your link.
        
           | steveklabnik wrote:
           | I will edit the post to make this more prominent sometime
           | soon :)
        
       | abahlo wrote:
       | Looks super great! Someone write this for Go please.
        
       | The_rationalist wrote:
       | Reminds me of https://github.com/bytedeco/javacpp
       | 
       |  _it maps naturally and efficiently many common features afforded
       | by the C++ language and often considered problematic, including
       | overloaded operators, class and function templates, callbacks
       | through function pointers, function objects (aka functors),
       | virtual functions and member function pointers, nested struct
       | definitions, variable length arguments, nested namespaces, large
       | data structures containing arbitrary cycles, virtual and multiple
       | inheritance, passing /returning by value/reference/string/vector,
       | anonymous unions, bit fields, exceptions, destructors and shared
       | or unique pointers (via either try-with-resources or garbage
       | collection), and documentation comments_
        
       | IshKebab wrote:
       | I use this. It's mostly excellent, though there are some awkward
       | bits, e.g. you can't return `std::string` or `std::vector` by
       | value from Rust to C++. You'll basically always end up with some
       | degree of copying stuff.
       | 
       | The build system is also confusing but I guess that's partly just
       | because it's a really complicated thing to do.
       | 
       | Still, beats doing it all by hand!
        
         | dtolnay wrote:
         | std::string and std::vector are not things that can exist by
         | value in Rust, because it's possible for them to be implemented
         | using internal pointers which are incompatible with how Rust
         | does move semantics.
         | 
         | Some easy and efficient ways to return a vector (or similarly
         | string) from Rust to C++ which do not involve moving it around
         | in Rust:
         | 
         | 1. A signature like `fn f() -> UniquePtr<CxxVector<T>>` i.e. a
         | return type of std::unique_ptr<std::vector<T>>, which is
         | trivially Rust-movable
         | 
         | 2. A signature like `fn f(out: Pin<&mut CxxVector<T>>)` with a
         | stack-allocated empty vector constructed by C++ for your Rust
         | to write the elements into, which is pinned and never moved by
         | Rust
         | 
         | 3. A signature like `fn f() -> Vec<T>` returning a Rust vector
         | i.e. rust::Vec<T> on the C++ side; rust::Vec has a fleshed out
         | API in C++ allowing it to be used like a C++ collection, with
         | iteration and indexing etc
        
           | zozbot234 wrote:
           | There is a 'transfer' crate adding support for custom non-
           | trivial moves of pinned objects, ala C++ move semantics.
           | AIUI, support for "in-place" objects would also be needed,
           | which is not yet part of Rust but there are plans to add it
           | to the language.
        
           | boris wrote:
           | I wouldn't call (1) efficient, especially for std::string
           | which itself might omit a dynamic allocation due to small
           | string optimization.
        
             | yazaddaruvala wrote:
             | It might not be truly zero-cost, but as a safe FFI it is
             | efficient. Meanwhile, does it really matter?
             | 
             | For any C++ project to integrate Rust the motivation would
             | have to be:
             | 
             | - The Rust code is specifically needed for the long-term.
             | 
             | - The FFI-boundary is temporary (i.e. always moving) as
             | more of the C++ is migrated to Rust. Ideally, checkpointing
             | the migration at optimal FFI boundaries. As such, any "in-
             | efficient" checkpoint should be cleaned up "soon", and dev
             | speed is more valuable from an FFI than perfectly zero-
             | cost.
             | 
             | - If for any reason a C++ project integrates Rust in a way
             | that is permanently through an FFI (I can't understand why
             | - maybe there is a really good crate or something), then
             | the development costs to use the unsafe non-ergonomic FFI
             | are worth the effort for this unique situation.
             | 
             | Very similar logic applies for any Rust project that
             | integrates with a C++ library for the long term. If the
             | team just needs a C++ library for the short term, the
             | efficiency likely also doesn't matter much.
        
               | dtolnay wrote:
               | > If for any reason a C++ project integrates Rust in a
               | way that is permanently through an FFI, then the
               | development costs to use the unsafe non-ergonomic FFI are
               | worth the effort for this unique situation.
               | 
               | I don't follow this train of thought; could you
               | elaborate? Why would I want _more_ unsafe code in my
               | project because it 's going to be there for longer? If
               | anything I'd want the opposite -- longevity of the unsafe
               | code being a reason _against_ wanting more of it sitting
               | around. I am just not sure how  "being worth the effort"
               | to write more unsafe code factors in.
               | 
               | FWIW the primary use case of CXX in my work codebase is
               | in long term hybrid-Rust-C++ projects in which dozens of
               | libraries using CXX (in both directions) are going to be
               | around for many years.
        
               | htfy96 wrote:
               | The motivations in claims unfortunately don't apply to my
               | case:
               | 
               | 1. FFI boundary will likely to exist forever in a
               | milions-of-line C++ codebase, especially when the
               | behavior of this system is not possible to be formally
               | specified / tested (e.g., depending on an unknown
               | external system, or some behaviors specified in hundreds
               | of pages "specs" full of jargons)
               | 
               | 2. In the above case, when C++ code dominates the FFI
               | cost would be signified as you need to call C++ routines
               | frequently to achieve stuffs. For example, when every
               | struct has some methods returning std::string the
               | std::string needs to be targeted.
               | 
               | In our case, the primary motivation of Rust isn't its
               | safety - we just use it for syntax sugars and ease of
               | extensions (with proc macros).
        
               | yazaddaruvala wrote:
               | > In our case, the primary motivation of Rust isn't its
               | safety - we just use it for syntax sugars and ease of
               | extensions (with proc macros).
               | 
               | Fair enough, but then "CXX -- ***safe*** FFI between Rust
               | and C++" (emphasis mine) is just not the right library
               | for you, and that is totally ok. It has a niche and your
               | usecase is different, both the library and your usecase
               | are in "the right".
               | 
               | > The motivations in claims unfortunately don't apply to
               | my case:
               | 
               | Is this situation different than what I called out
               | earlier?
               | 
               | > - If for any reason a C++ project integrates Rust in a
               | way that is permanently through an FFI (I can't
               | understand why - maybe there is a really good crate or
               | something), then the development costs to use the unsafe
               | non-ergonomic FFI are worth the effort for this unique
               | situation.
        
         | rvz wrote:
         | > The build system is also confusing but I guess that's partly
         | just because it's a really complicated thing to do.
         | 
         | > Still, beats doing it all by hand!
         | 
         | Still a pain to maintain, tweak and update your Rust project
         | and cargo configs with your C++ code alongside a third party
         | crate maintained by one person in the project. D lang is
         | probably the only language that has this built into into the
         | language.
         | 
         | I'd rather go for a first party library with this support or
         | C++ interop support built into the language like D.
        
           | mhh__ wrote:
           | D can even catch C++ exceptions on Linux (haven't tried it on
           | Windows yet)
        
             | lalaithion wrote:
             | https://cxx.rs/binding/result.html
             | 
             | You can also catch C++ exceptions (by turning them into the
             | idiomatic Result type in Rust) using cxx.
        
               | mhh__ wrote:
               | That's cheating!
               | 
               | https://d.godbolt.org/z/Ed65xMdj1
        
           | vardump wrote:
           | Might otherwise be a great idea (never tried D), but not very
           | useful if you have to integrate Rust with C++.
        
         | CodeMage wrote:
         | In your opinion, as someone who has experience using it, can
         | CXX make it easier to write Rust code that loads dynamically
         | linked plugins (also written in Rust)? Sorry if the question is
         | dumb, I started learning Rust only a month ago and one of the
         | things I ran into is that Rust doesn't guarantee stable ABI.
        
           | IshKebab wrote:
           | Probably not. It's not really designed for that, and
           | involving C++ when you don't need to is going to make it
           | really complicated!
           | 
           | I think you want something like this instead:
           | https://crates.io/crates/abi_stable
        
             | CodeMage wrote:
             | Thanks! I didn't know about that one.
        
       | ogoffart wrote:
       | Another create which i've been contributing to and maintaining is
       | the cpp crate, which can also be useful for interoperability with
       | C++, as it allows to embed C++ code snippets directly within rust
       | functions:
       | 
       | https://github.com/mystor/rust-cpp
        
       | gregwebs wrote:
       | This looks amazing. I wish it existed for C as well: C is
       | supported, but non-trivial C interaction is a lot of work with
       | Rust. Actually I wish it existed for Rust (for dynamic loading of
       | Rust libraries from Rust). Rust does not have a stable ABI and
       | the stable_abi crate creates a lot of additional work. I am
       | looking at using separate processes and using the shared_memory
       | crate.
        
         | afranchuk wrote:
         | I agree I wish that would be nicer, but I've found the
         | stable_abi crate to be quite convenient/easy for most
         | situations. It is extra work, but with derive support it's
         | often not too bad :)
        
       | quotemstr wrote:
       | One thing I've wanted to do for a while now is teach the Rust
       | compiler to interop with C++ by actually invoking the C++
       | compiler to specialize templates for Rust types.
        
       | choeger wrote:
       | I just glanced over it but if this does what it seems to do it is
       | really a great piece of work.
       | 
       | One of the biggest problems with typical FFIs is that they not
       | only use the C-ABI but also at some point invoke some C-API so
       | that the type safety features that both involved languages have,
       | but C does not have, are normally lost at the boundary.
       | 
       | Generating the C bindings from a description of the Rust and C++
       | interface is a really neat idea.
       | 
       | Now can this be used inside a standard C++ project? Could we
       | write pieces of code in Rust to gradually augment a C++ library?
        
         | nynx wrote:
         | Yep! That's the intention I think.
        
       ___________________________________________________________________
       (page generated 2021-03-25 23:01 UTC)