Live data from Hacker News

Rust and C++ Interoperability in Chrome

chromium.org

21–30 of 150 posts

Re: Rust and C++ Interoperability in Chrome

#21

Earlier quoted context omitted.

Rust does not have a textual substitution pre-processor, so like, you can do this sorta kinda but it would be way more involved.

> Rust does not have a textual substitution pre-processor Is that not pretty much what Rust's procedural macros are? [0] Except for the fact that they do not allow emitting invalid token streams (which is something you don't want anyways), it can do any arbitrary manipulation of source code, but in a way that is integrated with the compiler and less prone to breaking the program. [0] https://doc.rust-lang.org/referen…

its splitting hairs but not really. Rust's proc macros need to be explicitly invoked using special syntax at the usage site, either by annotating a statement or enclosing a block of tokens. On top of that, they do token replacement (or addition, with derive macros) which means they can only take valid input tokens as an argument (compared to macro_rules which can take arbitrary strings).

Textual substitution is less rigorous. It tells the compiler to replace a string with another, wherever it is seen. There's no restriction that it must be valid token input to be replaced or that it needs to be an explicit usage of the macro.

Re: Rust and C++ Interoperability in Chrome

#22

Earlier quoted context omitted.

Mozilla's uses a combination of https://github.com/eqrion/cbindgen and https://github.com/rust-lang/rust-bindgen depending on the direction of interop. These tools don't provide the safety guarantees of https://github.com/dtolnay/cxx but the also both predate cxx and C++ doesn't have much in terms of safety guarantees to begin with so the bar is pretty low.

It's also worth mentioning that Mozilla tends not to use a lot of standard C++ types, using nsTArray instead of std::vector or nsA?C?String instead of std::string. This makes the porting of APIs using arrays or structs easier, since Mozilla owns the ABI rather than needing to worry about different standard libraries having different ABIs.

C++ interop with C++ can be a pain. E.g. even with the same version of the same compiler, GCC can have different options enabled that affect the layout of some types.

Re: Rust and C++ Interoperability in Chrome

#23
post #19

> This seems to present some C++/Rust interoperability challenges which nobody else has faced. Is this different from the Firefox integration with Rust in some meaningful way? It looks like the cxx library is going to be critical for this. I’m curious how helpful others have found cxx for interop with C++? > For a Rustacean, this is controversial - all C++ is unsafe! But “unsafe” should be a really bad code smell. If…

I assume the cxx library still generates unsafe code, but that can be excluded from analysis, i.e. no human-written unsafe is needed.

Yes. It must use unsafe internally and/or in generated macro code.

This is good documentation on the safety of the library: https://github.com/dtolnay/cxx#safety

Re: Rust and C++ Interoperability in Chrome

#24

Earlier quoted context omitted.

Rust does not have a textual substitution pre-processor, so like, you can do this sorta kinda but it would be way more involved.

> Rust does not have a textual substitution pre-processor Is that not pretty much what Rust's procedural macros are? [0] Except for the fact that they do not allow emitting invalid token streams (which is something you don't want anyways), it can do any arbitrary manipulation of source code, but in a way that is integrated with the compiler and less prone to breaking the program. [0] https://doc.rust-lang.org/referen…

A proc macro would still need to be invoked as either

    cpp_call! { fn foo() { ... } }
or

   #[cpp_call] fn foo() { ... }
The former can be written even with a macro_rules macro. Regardless, both macros have to work by parsing the whole input tokentree `fn foo ( ) { ... }` and emitting it back with an `unsafe` prepended. The macro invocation cannot expand to just `unsafe`.

Re: Rust and C++ Interoperability in Chrome

#25
Back when I was an intern at Mozilla it was a real pain (in my opinion) to call between C++ and Rust. This was before bindgen and cbindgen, and certainly CXX.

Eg if you have a heap allocated thing that is passed between Rust and C++, who frees it? I ended up hacking something together, but it didn't feel right.

Re: Rust and C++ Interoperability in Chrome

#26

Earlier quoted context omitted.

Can't you just do a: #define CPP_CALL unsafe in the Rust equivalent to annotate "unsafe" cpp calls from Rust? "No boilerplate or redeclarations. No C++ annotations. Ideally, no allowlist." This seems like an unpractical approach. How do you even call C++ code from Rust without extern "C" linkage.

Really, your text macro substitution is missing the broader point. Hiding the word "unsafe" doesn't make it any less unsafe. The Rust compiler can't guarantee that the C++ code being called doesn't have use-after-free bugs or buffer overflows. The Rust compiler can't guarantee that pointers being returned from C++ code aren't just wild pointers that point in the middle of nowhere. The Rust compiler can't guarantee th…

> Ideally, you build an abstraction around the unsafe interop layer that rigorously enforces every requirement

I think that’s the reason that doc discusses using cxx, right?

> The Chromium document is right to call their idea controversial. Hiding the unsafety of the C++ code without actually doing anything to protect against malformed calls to that C++ code is a loaded footgun.

I agree with you, but at the same time, I think in this context, single codebase, abstractions built by the teams making the guarantees at the FFI boundaries... it feels like they would meet the requirements here? It definitely is less strict than Rust, but the way I interpret it is that they are saying they will guarantee the C++ is safe behind the abstractions (maybe they can mark the C++ In some way to state this), and that’s not all that different than what we say in general in Rust when we claim an unsafe call is in fact safe when we wrap it in unsafe.

What I mean by mark, is potentially use some annotation in C++ that cxx would only call into when that annotation is present.

Re: Rust and C++ Interoperability in Chrome

#27

Earlier quoted context omitted.

Can't you just do a: #define CPP_CALL unsafe in the Rust equivalent to annotate "unsafe" cpp calls from Rust? "No boilerplate or redeclarations. No C++ annotations. Ideally, no allowlist." This seems like an unpractical approach. How do you even call C++ code from Rust without extern "C" linkage.

Really, your text macro substitution is missing the broader point. Hiding the word "unsafe" doesn't make it any less unsafe. The Rust compiler can't guarantee that the C++ code being called doesn't have use-after-free bugs or buffer overflows. The Rust compiler can't guarantee that pointers being returned from C++ code aren't just wild pointers that point in the middle of nowhere. The Rust compiler can't guarantee th…

My point was that if you annotate code that's without the proper Rust guarantees because of cpp calls with a macro, you still can use the literal keyword "unsafe" and have it easily searchable for places where you use it because you have a good Rust internal reason. I.e. not to hide "unsafe" but to make it more clear where it is and because of what.

But they want "No C++ annotations". I believe they should have annotations in the Rust code to make it clear that the Rust/C/Cpp borders are crossed.

Re: Rust and C++ Interoperability in Chrome

#28

Back when I was an intern at Mozilla it was a real pain (in my opinion) to call between C++ and Rust. This was before bindgen and cbindgen, and certainly CXX. Eg if you have a heap allocated thing that is passed between Rust and C++, who frees it? I ended up hacking something together, but it didn't feel right.

>if you have a heap allocated thing that is passed between Rust and C++, who frees it?

Isn't this an issue regardless? Forget crossing the rust/c++ boundary, lets say you pass something to another class in c++, you need to have a contract of who frees what when, right? Maybe I'm not understanding the issue.

Re: Rust and C++ Interoperability in Chrome

#29

Earlier quoted context omitted.

Rust does not have a textual substitution pre-processor, so like, you can do this sorta kinda but it would be way more involved.

I keep meaning to dive into Rust, but it feels like they've removed so many useful things in the name of safety. I mean, I get it, these are all things which can be used to very easily cause chaos and shoot yourself in the foot. I look at code that I've written, though, and see so many challenges to writing the same thing in Rust. I spend 99% of my time in embedded Linux or bare metal, though, so maybe my view point…

Even modern C++ has eschewed macros for virtually every use case, the only reason I've had to use them (legitimately) has been conditional compilation and some compiler/platform specific issues.

Like if you're doing #define PI 3.14159 you're asking to get chirped in code review.

Re: Rust and C++ Interoperability in Chrome

#30

Earlier quoted context omitted.

Really, your text macro substitution is missing the broader point. Hiding the word "unsafe" doesn't make it any less unsafe. The Rust compiler can't guarantee that the C++ code being called doesn't have use-after-free bugs or buffer overflows. The Rust compiler can't guarantee that pointers being returned from C++ code aren't just wild pointers that point in the middle of nowhere. The Rust compiler can't guarantee th…

> Ideally, you build an abstraction around the unsafe interop layer that rigorously enforces every requirement I think that’s the reason that doc discusses using cxx, right? > The Chromium document is right to call their idea controversial. Hiding the unsafety of the C++ code without actually doing anything to protect against malformed calls to that C++ code is a loaded footgun. I agree with you, but at the same time…

> I think that’s the reason that doc discusses using cxx, right?

cxx can only really do as much as the C++ compiler can. It's certainly better than nothing. In my experience, a non-trivial amount of C++ code will have additional requirements on how and when you're allowed to call various functions; requirements that aren't enforced by the compiler.

>> It might be the right call for this specific case, though.

> I agree with you, but at the same time, I think in this context, single codebase, abstractions built by the teams making the guarantees at the FFI boundaries... it feels like they would meet the requirements here? It definitely is less strict than Rust, but the way I interpret it is that they are saying they will guarantee the C++ is safe behind the abstractions (maybe they can mark the C++ In some way to state this), and that’s not all that different than what we say in general in Rust when we claim an unsafe call is in fact safe when we wrap it in unsafe.

That's why I said it might be the right call in this specific situation. Normally, I don't think this is the right approach, but it's probably the sensible thing to do here, especially since it's likely the same team of people on both sides of the FFI boundary.

Post reply on HN