owned this note
owned this note
Published
Linked with GitHub
Date: 2025-09-02
## Fine-grained Rust/C++ interop presentation
- by Taylor Cramer & Tyler Mandry
- Overwhelming portion of large codebases are in C++
- You don't have to rewrite it all to get substantial gains in memory safety and reliability
- You won't be able to convert everything to Rust all at one for even moderately sized projects
- You'll need to communicate with C++ in the meantime
- Fish shell conversion from C++ to Rust - they replaced components gradually
- Interop: situation is messy, tool is limited
- How we can make Rust better for interop.
- Multiple interop tools today
- bindgen generates Rust API from C headers
- cbindgen generates C headers form Rust code
- both need to use C types only (no Rust enums, C++ classes, etc.)
- Lots of manual messy work
- cxx
- annotate module with cxx::bridge macro
- generates C++ and Rust API bindings
- you have to define the types cxx module
- zngur
- Users manually define their C++ and Rust API in zngur IDL
- can be passed across boundary without indirection
- crubit
- project from Google
- takes native compilation from Clang and rustc
- not every projects can use clang
- does not need to take header files, unlike bindgen
- Many distinct requerements
- IDLs let you define the interface explicitly
- C++ is a powerful and expressive language -- a lot of the extensions can't be implemented in Rust
- not everything should be made available in Rust
- but the flexibility has been a huge reason for its success
- Part 2: The missing pieces
- impossible to know if a C++ API is safe or not without seeing its implementation
- so all C++ headers must be marked as unsafe
- Option 1: apply a safe/unsafe annotation to the C++ code
- not always option: e.g. using a third-party library
- Option 2: type-based heuristics
- tools could roughly guess whether a function is safe or not based on the arguments
- not perfect, because a function could rely on hidden assumption
- crubit uses this
- handling C++ references is complicated
- lifetimes and aliasing not specified in C++
- Rust's mutable references have to be exclusive, not so in C++
- Differences between C++ and Rust in aliasing cause concerns if not careful when doing interop
- Rust prevents mutable aliased references but C++ allowes them, that means if a mutable reference is aliased it came from the C++ code
- The kinds of contracts in Rust cannot be expressed in the same way in C++, e.g. "if we have exclusive access to a type CppType, then we can have exclusive acccess to a type S" are not expressible in C++ in the same manner
Options to replace Rust reference types:
- raw pointers
- not satisfying: every C++ method takes a reference to `this`
- interior mutability via Cell
- it's not okay to project Cell through Option -- this would invalidate a reference
- can't use this in Rust for Option, Vec, Box
- Causes "classic" issues similar to these projections in C++, e.g. iterator invalidation
- adding C++ style reference type
- these references don't require exclusive access to mutate
- we've lost all Rust guarantees
- mutating it must be `unsafe`
- Puts the burden on the engineer to guarantee exclusively held if mutating
- Rust has issues that make it difficult to use reference types like this
- field projection would improve ergonomics
- autoreferencing doesn't work for custom reference types
- would require Rust compiler support for autoref of CppRef
- With some help from the Rust language, these three options to improve safety of using C++ references
Moving:
- Rust makes values movable by default
- C++ makes the opposidte decision: you must opt values into being movable via the move constructor
- Tends to be some work to move things / construct them the right way
- lot of C++ code uses things like self-referential types
- Pin can help here
- not very ergonomic right now
- gaining traction as a way to express non-movability in Rust
Overloading:
- Many C++ APIs use overloading and users need access to them
- Assumption of the ability in C++ to add overloads without breaking existing users; would push away C++ maintainers if not supported
- Rust doesn't have method overloading
- There is an imitation we can make using traits, but it has issues, just like C++
- adding proper overloading shouldn't add any new issues that we don't encounter already with traits
- When it comes to interop, you should be able to call overloaded functions in the other language
Calling Rust from C++:
- the crubit team automatically generates bindings from hundreds of crates
- use rust analyzer to understand Rust API and generate APIs
- Rust's stronger type system helps here a lot
- NOTE: Rust doesn't offer a stable ABI so the generated APIs need to be generated when the compiler is updated
- Having something like cpp_gen in the future alongside other Rust tools doing the above kind of work seems useful
- It's possible to support C++ uses of generic Rust types
- Limitation: if they've not seen an instance of using a type `T` in Rust, this will not be available to C++ either
- C++ templates are more difficult
- we could use unique types to generate Rust's impls
- for types passed by value (e.g. tuples) you can unpack the C++ `std::tuple` and repack it to a rust's `(..., ..., ...)` tuple types
- Unable to be used for pass by reference, because of unstable Rust ABI
The future:
- compiler integration
- e.g. make the internals of Clang talk to rustc's internals
- Swift uses integration with Clang to bind C++ code
- Rust could do the same
- C++ templating is kind of like a macro expansion
- Rust could call into clang to understand the C++ templating and generate Rust code
- combination of approaches, depending on needs and ability to use compilers (e.g. if able to use Clang or not)
Discussion:
- Pete LeVasseur: what do you think about Auto CXX?
- Taylor: Auto CXX is not being actively maintained
- almost deprecated, learned a lot from it
- crubit allows for passing a lot more values around language boundary and do a lot more things
- Nick DeMarco: elaborate a bit on the education side of things? The extent to which a C++ programmer needs to understand the Rust type
- Taylor: C++ programmers should be largely able to use Rust types as they would C++ types
- caveats: Rust types often don't support things moved_from
- e.g. Box should only hold non-movable types, so should not hold the solitary pointer to something in the same way you might in C++
- Jon Bauman: "overloading at home": how to support variadic overloads when Rust traits don't support that by default
- Taylor: you can support variadic number of arguments if you use Rust's (unstable) built-in `Fn` trait
- You can emulate this on stable if you accept an argument of tuple
## Rust @ Adobe: Zngur investigations
- by Nick deMarco
- script: https://github.com/nickpdemarco/rustconf2025
- Zngur: C++/Rust interop code
- Relatively new, ~300 stars, contributing for the last ~7 months or so
- Uses IDL file
- you have to define sizes and layout
- Rust compiler knows these, we have asserts to verify that they match
- generates `generated.h`, `src/generated.rs`, `generated.cpp` (sometimes)
- Unclear, sometimes `.cpp` file not generated
- be ready for these files to exist and deal with them in your projects
- The Omnibus model
- the FireFox team calls this the Rust Unified Library model
- 2 properties of Rust Crates
- 1. statically linking multiple `.a` built from Rust files is unsupported
- there are unmangled C symbols in Rust standard library that can/will conflict and cause ODR violations
- 2. Rust crates can bundle arbitrary C libraries
- no way to forbid this
- some discussion of not allowing `build.rs`, not gaining much traction
- FFI engineers need to assume Rust can bundle arbitrary `*-sys` libraries, unmangled symbols
- That means:
- we can either isolate each crate in a DLL with a stable C API
- or we can synthesise a crate which depends on all crates you need and re-export all public symbols
- Allows avoiding the ODR issues mentioned above
- Any Rust module linked into a C++ application must be behind a C++ library
- Doesn't apply if mono-repo, but many places are not
- allows for disjoint Rust crates in a single build tree
- minimized binary size overhead from duplicated Rust `std`
- one of the downsides of isolating each crate in DLL is having multiple copies of Rust standard library
- using a single DLL crate means we're using a single copy of Rust std
- We need a bunch of individual .zng files mapping to each crate
- Added feature to merge together multiple .zng files
- Partial union operation; works well in-practice
- Fails on contradiction
- Documentation included on how this merge process occurs
- Next steps
- cautiously optimistic we're ready for internal beta
- Guarantee zngur can be used in multiple transitive dependencies
- if two libraries I depend on use zngur, make sure they don't conflict
- Especially important for dependencies that are not under your control
- Emit separate headers for each subcrate
- a 60-line zngur example creates 4000-line `generated.h` files
- need to create a hierarchy of headers
- challenges to do with generics (e.g. put in `std.h`? `mylib.h`?) and avoiding causing circular dependencies
- Victor - Question: How do you handle multiple configs/features for a crate?
- A: we don't have support for that today, you'd have to build your own solution (maybe hacking something in build.rs?)
- Manish - Observation:
- Can be statically linked
- There are ways to work around a need for a mono-repo
- Could use crate patching instead of statically linking, expect the static link to occur later
- Noticing in Zngur
- Unidirection vs Bidirectional
- crubit: Bidirectional
- Zngur: seems more Unidirectional? C++ calling Rust?
- More flexibility if C++ calling Rust
- Nick: zngur does support decent Rust to C++ call too
- seems crubit and zngur are converging to a similar approach
- but crubit seems to be going in the direction of a single file
- and zngur is splitting it up
- Taylor - Comment:
- Invoking Rust from C++ is much simpler
- For this direction not looking to use IDL
- Ideal is in a number of months to be using same tool to pass .rlib or .rmeta and get the right thing out the side for calling Rust from C++
- Hope to have somethning usable by not just them soon
- Michael - Question:
- Is growth of C++ .h linear or super-linear with respect to .zng contents?
- A: not certain, it seems to be a great initial growht and then linear
- Michael: crubit also generates a lot of code; maybe this is just inherent?
- Tyler - Question: are all DLLs sharing shared version of the standard library or using different versions?
- A: Shared technology teams unwilling to lock; may need DLL boundary in that case for that reason
- Jon - Question: As an outsider, the biggest differentiator seems to be having the monorepo having full control over the world. To benefit the Rust community overall: where can we overlap with one another and share? What are the things where everyone does their own thing and which ones are common enough to try and solve for everyone
- David:
- Monorepo is probably not the biggest differentiator
- Monorepo need something you don't when not monorepo
- Making sure deps don't conflict
- Biggest differentiator: calling C++ from Rust
- zngur: define interface in .zng, then implement how you want
- crubit: tries to do a direct representation of the C++ world in Rust
- Calling Rust from C++ there's a lot of opportunity to converge on stuff
- how to represent Rust types in C++
- what would an IDL look like, these sorts of things seem open for discussion
- Tyler: are you saying that the final interface you want Rust users to use is what's in the .zng file? Or do you have Rust-specific wrapper around that to make more idiomatic?
- David: Rust interface into .zng file is intended to be a "Rusty" API
- e.g. indexing should be safe in Rust, C++ would do this bounds check
- that way Rust is not faced with "ugliness" of doing these kinds of invariant checks
- Jon - Question: Dichotomy David proposed, more about separating the directionality of the calling, C++ to Rust or vice-versa?
- Significantly different handling in C++
- Different in how we (google/crubit) expect to use Rust vs. the rest of the world
- Rust takes over gradually from C++
- you start with a couple functions, build larger and larger modules and eventually end up with everything in Rust
- practical issues when needing types from C++, end up in a bit of dependency hell
- when you're able to enforce these strong API boundaries, it helps you see which interop operations are safe, where to insert guards
- Good ability to limit with versions of APIs to make sure new users in Rust don't make a mistake
- you wind up with a divergence of what API are available to your Rust code and what APIs are available to your C++
- we have billions lines of C++
- we want to get to a situation where bindings to every C++ type are going to be everywhere for every Rust user
- we've seen a lot of success with Android, Fuchsia where there's a stable interface e.g. over an IPC boundary
- but a lot of our code is not like that and we want to succeed there too
- Devin - Comment - Differences of approach. There's the interop, automatic vs. IDL, monorepo, what types should you use?
- policy decisions, u8 vs u8int_t
- some of these are not going away
- configuration options seem important for adoption of tooling
- Victor - Question - From what I understood from Nick, at Adobe they prefere DLL-level granularity, rather than function level migration. Did I get that right?
- David: our approach is doing things at a coarser level
- someone's writing a new feature that involves creation of multiple files
- the majority will be done in Rust and you'll create C++ bindings
- should then have coherent way of working with C++
- implementing just a single function in Rust would be far more messy
- For us, it's somewhere inbetween DLL-level granuality and function-level migration
- More configurability when you want to call C++ from Rust, less configurability when you're calling Rust from C++
- Nick DeMarco seconds this in chat
- You should be able to look at a Rust definition and know exactly how to call it from C++
- Michael - Comment - Generated surfaces being idiomatic or not discussions
- You can have an interop set up such that you define a C++ interface and get a perfect Rust definition back
- The interop tools can do that when configurable enough
- Examples at Google: crubit does a "reasonable" projection to C++ of what's going on in Rust
- fundamental challenges with that: aliasing, movability, etc.
- the long tail is using generated bindings entirely
- but right now there is a layer of additional Rust code to make things nicer
- Tyler: "unblock people" from adopting Rust
- Victor - Comment - We might be closer to what Adobe described re granularity level
- Did experiments across spectrum
- ad-hoc interop got teams into trouble long-term
- didn't properly design interop layer
- architectural boundary from C++
- opportunistic rewrites of sticking in Rust were a maintenance nightmare
- Either backtrack or write more in Rust to make it worthwhile
- more successful: making these rewrites and inetrop more dilligently
- When nicely carved out into e.g. a DLL generally saw a better outcome
- regardles of the direction, this resulted in a better outcome
- less about the mechanics of interop and more about bad decisions and opportunistic interop
- integrating Rust here and there in various components doesn't help in reasoning about soundness and safety
- Rust could improve this, but when bringing in fine-grained interop, tend to be able to prove less of the ability to prove soundness and safety
- sometimes we need to put temporary scaffolding, but overall the plan is to remove that scaffolding
- Dmytro - Comment - Spectrum of interop e.g. all the way from function to .DLL to RPC
- But we cannot go towards these coarser granularity stages -- especially for monolitic applications -- then we need to push towards fine-grained approaches
- what are the maintenance issues you ran into with this approach
- Victor: We need to highlight use-case difference between e.g. Google and elsewhere
- rarely large binaries or .DLLs
- we tend to have smaller things
- can't attack in a single sitting, but we're less concerned about dealing with monolithic things
- even a microservice tends to be pieced together from multiple other things / libraries
- our maintenance problems:
- controlling unsafe boundaries and being able to reason about them
- and reasoning about undefined behaviour
- internal tools we're using are not fully ready for this hybrid world
- made progress in last 4-5 years to support Rust, but not yet near tier 1 support in the company
- this is an area where teams struggle to find paved paths to set up infra, signal handling, etc.
- internal tools for compliance checking, etc
- hard to test and run the tools internally when you have two Rust functions calling a component
- indicator of us not being fully ready to support these engineering teams
- not a symptom of process, engineering system limitations
- ship more to engineering teams, find they do some new thing never seen
- a learning process of how best to support teams
## Improving Rust/C++ Interop with Trivial Relocatability
- speaker: David Sankel
- [slides](https://rust-lang.zulipchat.com/user_uploads/4715/EXbjCZVDxiw7ISc-1eXZJFmd/Improving-Rust_C-Interop-with-Trivial-Relocatability.pdf)
- goals
- instantiate most C++ on the Rust stack
- avoid allocation
- avoid undesirable Pin ergonomics
- be portable
- relocation comparison between Rust and C++
- table presented for this comparison
- Taylor: cannot move from local and then read/write
- Manish: Can write to uninitialized, but not moved memory
- difference in how moves function: C++ gives you a distinct object for "moved from" that is destructible
- `std::memcpy`: trivially copyable
- gives you a trivially destructible distinct object
- in C++ must use `std::trivially_relocatable` otherwise Undefined Behavior
- ARM64e ABI has pointer authentication codes to virtual table pointers
- codes are salted with pointer's address
- all polymorphic C++ objects are self-referential on that platform
- why?
- Common attack vector is to rewrite vtable pointer to allow calling into bad code; this is intended to prevent that
- `std::start_lifetime_at`
- Allow to copy data to new location
- So long as you call this function before using it
- Takes first parameter of where it used to live and then where the memory currently sits at
- Validates vtables are valid
- recreates the salt for vtable pointers
- If you have a pointer to object in C++
- if converting pointer to uint_t
- then it's not UB
- Manish: where does the name come from?
- David: in C++ there's a set of functions, e.g. `start_lifetime_as`; fits into existing naming scheme
- `std::start_lifetime_at` doesn't do the memcpy, instead you call it afterwards
- intention to separate concept of copy and marking the copy as having been done
- Works with realloc as well
- Dmytro: why did we consider it necessary to support objects with vtables?
- Fix seems to not consider vtable-based objects not trivially relocatable
- David: Very intense debate around concept of trivial relocatability in C++ standards team
- Competing proposal excluded polymorphic objects; this removes a large use case
- Therefore we have the rationale for this fix-up code
- Zero support for removing support for polymorphic types
- Last round fo NB comments; unlikely to change now
- Good for Rust, so then allows for more Rust types to get constructed on the stack
- Tyler: who calls this function? when is it called?
- covered in following slides
- Jon: How to avoid attacker exploiting?
- Maintains integrity of object: when making a virtual function call... (explain during examples)
- Tyler: What's the wider use case for trivial relocatability for polymorphic objects?
- David: If you have `std:Vector` implementation and wanted trivial relocatability for memcpy when changing size of buffer would be wanted/needed
- Manish: So much use of virtual pointers in code seeing later; if trivial relocatability not present for polymorphic objects this would be unfortunate
- Devin: The fact that they are polymorphic means their API by definition cannot be as nice in Rust
- some example code shown for polymorphic functions in a hierarchy: shape, circle, ...
- need both trivially relocatable and trivially replaceable in C++
- Defined in C++26 specification; was implemented previously in libraries and was all UB
- Taylor: is this solving problems people have? Copying large chunks of memory don't seem to be helped?
- David: On ARM64 there needs to be some iteration and fix-up for the salting; other platforms this becomes a memcpy
- in Rust side there's both "data" and "origin", pointer to where this started from
- `start_lifetime_at` will be a no-op on most platforms
- Taylor: two branches of concerns
- 1. building location into object means can no longer represent objects of Circle type that are the same as in C++
- Storing data out of band?
- Some way to fix up?
- Two version of circle or a wrapper type?
- 2. even `const` functions on object will perform mutation where they didn't previously
- could be introducing data races
- code previously thread-compatible would be thread unsafe
- could be resolved with atomics or some clever mechanism when races could occur
- Devin: First topic API-wise get same heap-allocation concept, but performance is better
- Manish: performance not absolute; better than heap in that you reduce allocations and indirections, loads
- Introduces FFI calls depending
- If you have a lot of fix-ups then you could be interfering with the optimizer
- Taylor: could get clever: if doing a simple field accessor and determining
- Manish: vtable pointers are implementation-specific along what choices they are allowed to make; other considerations?
- David: currently only one ABI which is doing this
- that platform is also impacted
- carrying around an extra origin pointer in the cache
- if you know platform doesn't require it, may be able to signal on the interop layer that it doesn't matter
- goal is to write portable code
- you're already paying cost by enabling the salting of vtable pointers
- David: attack surface or rewriting vtable pointers
- if overwritten, then can determine that it's been tampered with
- previous state must be valid
- because origin provided, this can be checked for
- Manish: inspecting origin pointer, ensuring it has the right form, then going to destination pointer and doing the fix-up
- Look at destination vtable pointers, confirm valid according to the origin
- origin is not read/written, only compared
- David: security trampoline problem
- you could have a signed version to the "evil" place
- requires extra knowledge of where the original place was
- validates existing vtable pointer before signing up the new one
- Taylor: you could ship a simple memcpy version in theory, but it would be a bad idea for that platform: ARM64e
- If security vulnerability elsewhere in C++ program you could jump to the "free vtable" portion and write in there
- David: Oliver Hunt pointed out you really need the origin for that reason
- Taylor: whenever you jump to C++ anything you pass to it you need to make sure it's good
- Dmytro: what prevents someone from doing jumps into the function after check done and succeeded for `start_lifetime_at`?
- David: this is made atomic on the ARM64e platform
- yes, Oliver Hunt who invented ARM64e platform confirmed this
- Jon: point is to make authentication and signing atomic into one operation
- David: comes back to the underlying model being atomic
- Taylor: concern about atomics: still need to update local pointer to allow for concurrent reads from objects to be safe, right?
- David: have to give more thought; only happening in C++-side
- Taylor: two calls from different threads of `const` functions
- Michael: have to start considering ISA-level
- Taylor: need some sort of spin-lock for whoever's waiting on `start_lifetime_at`
- One of the callers would observe the wrong origin?
- Manish: data pointer must be old data and updated to new data
- Applying the same mutation twice is idempotent; the check is not
- Dmytro: first write is to vtable pointer, second is to the origin
- Like Taylor was said, both need to be read, then checked and updated together
- Taylor: one caller passes, spin-lock on final value being complete? Could be made wait-free, but wrinkles
- Manish: semantics that other ABIs could add
- requiring all of those ABIs to be atomic is a constraint to be aware of
- David: other option is to make `const` operations non-`const`
- Make copy of object before calling this
- Michael: example of shape, circle: if how we represent type across C++ boundary could be painful
- Could be useful adapter / container type
- Some generic of T
- Similar to how there's a dropped flag that was discussed earlier
- Rust API immutably borrows
- Hands out reference for usage
- Quinn: is the Circle being moved somewhere?
- Rust does this move on the assignment `a = b`
- Dmytro: if there's an architectural guarantee to ensure a single instruction is available, it needs to receive a *pointer* to the origin, not origin as a value
- Probably `start_lifetime_at` should receive a different API
- Tyler: visualize state diagram that works if verifies and signs on failure is a no-op
- skeptical on a way to implement in a way that's atomically-safe
- Taylor: seems not read-modify-write; a thread could end up waiting on another one
- Dmytro: attacker could build a primitive to break the hash
- David: only need to look at origin only if not already good
- Taylor: need to know if you succeeded or not and thus who should update the origin
- Taylor: can we make usage of `pin` nicer to skip `start_lifetime_at`; feels like this creates a new class of types not understood
- Many C++ folks may not understand which types are trivially relocatable and not
- Good if able to have location-sensitive APIs to use from Rust to interact consistently
- David: It's a both-and from his point of view
- Jon: to help clarify -- where is this needed? Would pinned types with better ergonomics help?
- Manish: where Pin is right now: 2 things: 1. confusing 2. hard to use
- Confusing may remain, but could get easier to use
- Taylor: once in "normal" code can make this a bit easier to pattern-match on
- Tyler: build into the borrow-checker to note when pinned
- should give better experience
- optimistic around this path
- Devin: worried people may think - make it valid to use in C++ something that's been Rust-moved
- Having to call `start_lifetime_at` even within Rust types / semantics seems tough
- or add wrapper types
- Asking implementors to allow us to abuse the C++ implementation
- C++ type that you generate
- it is UB to do what Rust does
- but in Rust you should be able to do Rust moves
- Taylor: C++ definition of type allows you to do non-trivial things in destructors
- Would allow for Rust trivial copy-ability
- Technically UB?
- Jon: to help clarify -- where is this needed? Would pinned types with better ergonomics help?
- Could use C++ functions to move them
- Doable today, but have to call a function or put a proc macro to do the call
- David: pinning is viral; as soon as you have to hold a pin then you end up pushing it out to what holds this too
- Jon: seems like the approach is trying to hide from Rust what's going on, solving in a different way than Rust solves it.
- David: complication on the backend, but from a user perspective "it just kinda works"
- ergonomics is important; if there's friction people will be writing more C++
- Devin: Only the most derived type would be moved, right?
- David: yes
- Jon: seems to be security questions; should we loop in security folks like Oliver?
- Taylor: concurrency concern not addressed
- Jon: but we can agree that if security is addressed, then Pablo's simplification worthwhile
- David: Clang already has trivially relocatable working
- For ARM64e turning on for internal libraries, not exposed yet
- C++ trivial relocatability is useful to C++; interop with Rust was not a chief concern
- Dmytro: question about simplified API
- David: upcasting into pinned value is where you'd use `start_lifetime_as`; also applies to pointer casts
- Devin: can never be moved again; would lose track of most derived type
- Taylor: Would not want to move base class in C++ anyway
- Tyler: seems should be able to have ownership of derived, upcast pointer of base to that derived, potentially move or perform operations on derived later
- David: Heard back from Oliver and he doesn't present any security concerns with `start_lifetime_as`
## What is `u8` in C++?
Devin: In C++ a uint8_t could be neither a char or unsigned char
- in practice they almost always are
David: "Rust namespace" with fundamental types in it for C++ and "C++ module" with fundamental types in it for Rust
- C++ could have implicit conversions to act like they are the expected type
- Calling C++ via Rust would want to talk about e.g. CppInt type due to different sizes etc
David: distributing a specification of a Rust namespace in C++ and clarify which ones have same behavior
- Devin: on 128 bit platforms there are some considerations
- Michael: who's the audience of the specification?
- Taylor: whatever your interop system is
- Tyler: could go further and push into the Rust std distribution
- Devin: would expect to live in Rust lang repo
- Taylor: Rust lang nursery; want to have some authority
[ffi_11](https://docs.rs/ffi_11/latest/ffi_11/)
> In ffi_11, if a type is distinct in C/C++, it is distinct in Rust. This relationship varies from platform to platform.
* it thinks
Taylor: should be one library that gives authoritative guidance on what maps and how from one language to another
- Tyler: Rust and C++ libraries must agree on choices they make for represetation
- On Rust side want all new types to be defined by a single libary
- Taylor: want a big table per platform / target triple / more specific
- What's needed in C++, Rust is generated
- David: specification is important, since someone might have a new platform and need to make this mechanism work
- Taylor: don't want fragmentation across multiple GitHub repos
- David: for the forseeable future there will probably be a single implementation, but don't want to close the door
- Rust does have a "single implementation" aspect, whereas C++ does not
- Taylor: WASM is an extreme case, but even in the case of GNU Windows vs MSVC you have in some way a different definition of "platform" even though they will both run
- David: if special hardware support for e.g. tuples could have variety of different implementations
- Taylor: "abstract specification" that forces folks to agree and try to implement seems hard
- David: if zngur can target implementation, seems like a clean breaking point; not as concerned about implementation divergence
- Michael: how many axes do we have that can differ, e.g. C++ compiler, Rust compiler version
- How can we ensure that we write the headers (.h) portably?
Taylor: leave 128-bit integers, smaller machine learning floats off the list for now?
- Devin: These cases seem interesting due to hardware possibilities
- Taylor: in Rust compiler currently best-effort for target, if not present, just don't support 128-bit integers
- Taylor: Support with extension point to allow platform that the Rust compiler doesn't know about to extend to support
Jon: if you want the specification to be "controlling" you probably want at least one more implementation beyond the reference implementation to ensure that the implementation follows from the spec
- If you want the specification to be controlling and the reference implementation ends up differing, this is then a bug in the reference impl
- Devin: ladder of #ifdef seems to form the specification
- Taylor: abstract rules written down seem valuable
## Path forward for short-to-medium term:
- proposal need be made for a team to maintain, e.g. libs/interop
- put ffi_11 crate in rust-lang repo (perhaps as-is? with modification?)
- for C++ types usable in Rust
- have corresponding header in the same repo to express Rust types in the standard language of C++
- Pete: What about older standards, e.g. Aerospace and Automotive tend to pin to decade+ old versions like C++14 :upside_down_face:
- Taylor: polyfill for many missing things in std lib
- Devin: C++17 adds nice features useful for communicating movability; may be workarounds
- Language gaps
- std::indirect and std::polymorphic are great for std::unique_ptr replacement, but in C++26
- One for replacement of std::function
- goal: have tools like zngur and crubit talk the same underlying types so mix-and-match and interoperability between these tools is achieved
- substrate layer so tools can build on top of it
- Rust project specific responsibility to maintain this mapping for various compiler vendors
- Include extension points for compiler vendor / platform which is unknown
David: we should explicitly think about how far back to support C++ versions
-Taylor: polyfills for things which we want to have nice ergonomics