7 ms·
> 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
by bluejekyll 6y ago
> 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 “unsafe” is needed for all C++ calls, there will be thousands of them, and the “unsafe” keyword will lose its meaning.
I don’t think the attitude about unsafe usage differs from the general Rust community. If there’s a lot of it, then it definitely is a code smell.
- rightbyte 6y agoCan'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.
- deleted 6y ago[deleted]
- steveklabnik 6y agoRust does not have a textual substitution pre-processor, so like, you can do this sorta kinda but it would be way more involved.
- roblabla 6y agomacro_rules! cpp_call { ($($tt:tt)*) => { unsafe { $($tt)* } } } Yeah, not pretty, but it works :P
- rightbyte 6y agoNature finds its ways. Seriously though, maybe comment annotation would be better than abusing the macros like that. I though there was a more direct cpp way. (Edit: As in the c preprocessor, not C++.)
- luizfelberti 6y ago> 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/reference/procedural-macros.html https://doc.rust-lang.org/reference/procedural-macros.html
- qppo 6y agoits 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.
- Arnavion 6y agoA 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`.
- steveklabnik 6y agoYes, this is what I was meaning, thank you.
- ComputerGuru 6y ago> Except for the fact that they do not allow emitting invalid token streams (which is something you don't want anyways) Reminds me of some of stringification abused in the Linux kernel. What looks like a function call passing the literal text param1## as an argument, where it would then trigger a different stringification in a subsequent preprocessor pass that prepended ##foo to the passed argument.
- fullstop 6y agoI 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 is a bit skewed. There seem to be very few GUI libraries which can be used in Rust as well. I'll keep checking https://areweguiyet.com/ https://areweguiyet.com/
- ChrisSD 6y agoIf you want to use a tool that pastes code fragments in arbitrary places you can. However, I'm not clear on the benefit over something that's aware of its context, or at the very least AST aware.
- qppo 6y agoEven 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.
- rightbyte 6y agoYe, everyone know it is: #define PI 3.1415927410125732421875 that is the float closest to pi or that there is the GNU extension USE_FUNKY_DEFINES-something with M_PI for cmath. (I know you meant constexpr:ions).
- Const-me 6y agoLong time ago when computers were 32-bit I used to write something like this in C++: inline double pi() { __asm fldpi; } On old enough compilers, this will get you 80-bit version of pi, more precision than even double. Completely useless now, modern compilers no longer emit x87 code.
- 6y ago
- coder543 6y agoReally, 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 that the C++ code isn't mutating into an immutable reference. The Rust compiler can't guarantee that the C++ code isn't holding onto a reference to an object that it will later mutate when Rust thinks it is now the sole owner of that object. The Rust compiler can't guarantee that the C++ code isn't sending un-Send objects across threads, or that it isn't deallocating objects that it isn't supposed to deallocate. Rust makes a lot of guarantees about code written in Rust, which is why people are so passionate about it. The Rust compiler feels like having the world's best static code analyzer at your fingertips. It's really nice. C and C++, on the other hand, are each full of an assortment of powerful footguns. Well-written C or C++ code interops very nicely with Rust, but the problem is that programmers are only human, and humans have been repeatedly shown to make mistakes. So, calling C++ code is unsafe, and unsafe is a code smell because it is an opportunity for guarantees to be violated, which is undefined behavior. Ideally, you build an abstraction around the unsafe interop layer that rigorously enforces every requirement that the C or C++ code expects (but that the C and C++ compilers cannot enforce), so that the external code will behave as well as it can. 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. It might be the right call for this specific case, though.
- bluejekyll 6y ago> 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.
- the8472 6y agoI assume the cxx library still generates unsafe code, but that can be excluded from analysis, i.e. no human-written unsafe is needed.
- bluejekyll 6y agoYes. 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 https://github.com/dtolnay/cxx#safety
- modeless 6y agoSeems like the difference is the level of friction the project is willing to take on in order to support Rust.
- atoav 6y agoI use Rust. Unsafe is okay as long as there is a comment detailing why it is okay. Just like .unwrap() is okay for stuff that never fails. What is indeed bad is code that sprinkles unsafe all over the place without explanation and if you check a random segment fails to handle common error cases that pure Rust would force you to handle.
- dllthomas 6y ago> Just like .unwrap() is okay for stuff that never fails. Never fails, and will also never fail in the future as other code changes. There are certainly situations where you can be sufficiently confident of that, but it's something to think about.