7 ms·
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
by coder543 6y ago
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 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.
- coder543 6y ago> 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.
- rightbyte 6y agoMy 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.
- lalaithion 6y agoI disagree; All of the things you say about C++ code also apply to unsafe Rust code. There's an unsafe keyword that allows using that unsafe code, and when you use it, you are asserting to the compiler that "I promise this code is actually being used in a safe way". All this document says is that the C++/Rust boundary should be considered another place where you are asserting "I promise this code is actually being used in a safe way".
- coder543 6y ago> All of the things you say about C++ code also apply to unsafe Rust code. Which is why unsafe is considered a code smell, and why people in the Rust community get upset when unsafe is used unnecessarily. That's why some people might find it controversial to hide the unsafety of the C++ code being used here. I've used Rust professionally for years, and I don't remember ever having to write any unsafe code in a professional context apart from FFI, and even then... the glue code to wrap around that unsafe FFI was very strictly written to make things as comfortable as possible for the FFI code and minimize the chance of bad things happening on the other side of the FFI barrier. However, even then, your statement is not entirely correct. Unsafe Rust is extremely similar to safe Rust, enforcing the vast majority of safety requirements automatically. Unsafe code is very limited in what bad things it can do compared to C++ code. https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#unsafe-superpowers https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#unsa... - Dereference a raw pointer - Call an unsafe function or method - Access or modify a mutable static variable - Implement an unsafe trait - Access fields of unions Those are the only things unsafe Rust can do that safe Rust can't. It doesn't turn off the borrow checker or anything else. There are a lot of mistakes that can be made in normal C++ code that you can't make in regular, safe Rust. > All this document says is that the C++/Rust boundary should be considered another place where you are asserting "I promise this code is actually being used in a safe way". The problem is that C++ compilers are much less capable of enforcing safety (inherently, due to legacy design decisions and backwards compatibility), so you often want a layer of glue code that only allows you to use C++ code in ways that are known to be safe, not just any arbitrary way that the function might be callable. I already said I agree Chromium's team seems to be making the right call for this specific situation. It's not the right call most of the time. C++ code isn't fungible as being "just as good" as safe Rust code. The unsafety needs to be obvious, just like it is with unsafe Rust, so that you can recognize that the external C++ code (or unsafe Rsut) is a loaded footgun, and to treat it with appropriate care. To be clear: Rust isn't a perfect language, and you can get the vast majority of the safety benefits by using almost any garbage collected language. What makes Rust awesome is that you can bring the memory safety of garbage collected languages into the realm of consistent performance requirements that previously was inhabited mostly by C and C++. Rust is a nice language, but so are a lot of other languages.