The difference is that with rusts safety you can to some degree "try thinks out". Which doesn't work with C++ as you might have hidden UB.
Also C++ has a bunch of "hidden" features and unexpected interactions with other features and UB like e.g. forward guarantee because of which `while(1);` is UB.
I would like my application to be easy to distribute. I was hoping to distribute it as a single executable file. I don't think this is possible with the LGPL?
You would distribute it anyway as one file, because you're going to pack it into something which does the install. i.e. dmg/pkg file, installer, Linux package, etc.
I don't think I've ever seen a Mac app which consisted of only one file. The whole idea behind .app apps is that they consist of several files.
> ... std::atomic<bool>. This tells the compiler not to elide reads and writes of this variable, ...
If I'm not mistaken, this is not true. The compiler is still allowed to elide reads and writes on atomic variables. (For example merge two consecutive writes, or remove unused reads)
The point is that the C++ code should be safe because the C++ programmer should not introduce UB on its C++ code.
If the C++ code invoke UB, that is a bug in the C++ code which should be found by reviewing the C++ code alone.
No need to write 'unsafe' because .cpp files are already known to need carefull review.
> The point is that the C++ code should be safe because the C++ programmer should not introduce UB on its C++ code.
That's a misunderstanding of safety, and ub, and `unsafe`.
The C++ code could be unsafe when called with certain values which it is not normally called with. This is common. This is also not allowed in Rust, it'd be unsound.
Furthermore C++ has different notions of safety than Rust. C++ allows dangling and null pointers (whether raw or smart), it doesn't allow calling them. Rust does not allow dangling or null pointers unless they're raw. You can have a null unique_ptr, you can not have an empty Box.
The cxx crate and the autocxx tool should make sure that the exposed C++ functions only take arguments types which have well defined semantics.
In your example, a rust Box<T> maps to a rust::Box<T> in C++, which cannot be null. And a unique_ptr from C++ maps to a cxx::UniquePtr in rust which can be empty.
If somehow the C++ code puts a dangling or null pointer into a rust::Box, that is clearly a bug in the C++ code.
I agree with you that by controlling both sides of the FFI (the Rust and the C++ code) one can make sure that the types work.
The real problem is, however, that C++ lacks an "unsafe" keyword, so functions like:
/// # Unsafe
///
/// Must call `bar` after a sequence of calls to `foo`
unsafe fn foo();
fn bar();
just look like
/// note: must call bar after a sequence of calls to foo
void foo();
void bar();
You can autogenerate "correct" C++ code from that Rust code (just loose the "unsafe"), but you cannot autogenerate safe Rust code from that C++ code unless you start parsing and understanding documentation comments (which could be possible, e.g., chromium could annotate C++ APIs that should be unsafe in Rust).
To generate Rust from C++, it does not suffice to just "look at the types" like cxx and autocxx do. One also _at least_ need to read all the API documentation comments, check if there are any invariants that must be preserved, and act accordingly.
If the APIs are ok and can be wrapped mechanically, the actual wrapping can be made trivial with tools, but there is no tool today that will tell you whether this is the case.
That is, at the end of the day, if you need to expose 10k C++ APIs from Rust, you will still need to manually inspect those 10k C++ APIs, and _think_ about whether they are safe or not.
That's the time consuming part, and you actually want to only do this once, and write down why an API is safe or not, so that other programmers don't have to repeat this work every time you hit an FFI issue.
So IMO while cxx and autocxx are "ergonomic", they spare you only the easy lest time consuming portion of the work. autocxx also makes it easy for you to either not check, or not write down the result of the check, and this could end up creating a lot more work down the road.
---
Note that this is something one wants to do even when one trusts that the C++ code is correct. In the example above, the C++ APIs can be correct, but one can still UB by using them incorrectly.
C++ code only needs to be safe according to C++ rules (not Rust rules). So it is possible for the C++ to be safe, and the corresponding Rust code to be unsafe, e.g.,
* int foo(); which returns an uninitialized int is OK according to C++ rules, but would need a MaybeUninit<c_int> according to Rust rules.
* int foo(); could throw an exception, causing UB in Rust, since Rust assumes FFI declarations not to throw according to the spec. Rust can only export `noexcept(true)` C++ FFI declarations, or C functions (since C cannot throw). Apparently, autocxx and the cxx crate ignore this and treat all C++ functions as if they never throw, giving them a safe API. That's unsound. (One can fix that on nightly Rust though).
Unsafety can also be introduced through ABI incompatibilities, but IIUC autocxx usage of rust-bindgen deals with that.
Imagine what proponents to heliocentrism such as Galileo had to face explaining their theory and how it explain a simplified elliptic orbit for the planets rather than the strange curly orbits known by geocentrists.
They'd be confronted with basic question from the audience: Why would things fall down if it was not the center? (That was before Newton) Wouldn't we see paralax in the stars (stars are much further away than what was believed at the time). Wouldn't we feel it if earth turns so fast? "Maybe that explains the tides" said Galileo (but it doesn't)
The previous model was also explaining the observations quite well at the time. Why should we care for another theory? (God works in mysterious ways.)
I'm not saying that MOND is correct. ("they also laughed at Bozo the Clown".) Just that the fact that there are some unexplained missing piece does not mean one should reject it so quickly.
Comparing oneself to Galileo is generally viewed as a sign of crackpotery, especially if it's used as a response to legitimate criticism.
> Just that the fact that there are some unexplained missing piece does not mean one should reject it so quickly.
It's not just one missing piece. It's a whole series of basic properties of the observed universe. Most MOND theories are tailored to match one particular observation, but fail to match everything else. Until there's a MOND theory that matches a basic set of observations (like CMB anisotropies and the large-scale structure of the Universe, the ratios of abundances of the light elements, weak lensing measurements, etc.), MOND is simply uninteresting to most astrophysicists.
> Imagine what proponents to heliocentrism such as Galileo had to face explaining their theory and how it explain a simplified elliptic orbit for the planets rather than the strange curly orbits known by geocentrists.
They did. In fact, the audience was quite more brutal. And it did not only delay the progress of physics, but also destroy the local research, leading to the entire community being rebuilt on England.
But, well, as you said, we always have to remember they also laughed about Bozo the Clown.
Yeah, too bad it is clear and straight down to business, readable by any version of every browser and since the text color is black instead of light grey one can actually read it.
The guy of Bellard's caliber does not need flying unicorns on a screen to attract people.
Is that not the same in C++ and in most languages?
... until someone else in your team uses it in your code base. And then you need to know about it in Rust as well.