Hacker News new | past | comments | ask | show | jobs | submit login

I've been planning on the cxx side to update my attribute macro to require `unsafe mod ffi {...}` or `unsafe extern "C" {...}` to signal presence of a proof obligation. I think that resolves all concerns from the January discussion, including viewpoints I don't necessarily agree with, without sacrificing any ease of use.

Just haven't found time to make the compiler PR to allow exposing that syntax to macros yet, but I will.

That would completely resolve my concern about cxx.

(i still think its a soundness bug in Rust that `extern { }` does not require unsafe since one can use it to trigger UB in safe Rust code)

What's your thoughts on #[no_mangle]? https://github.com/rust-lang/rust/issues/28179

Frankly Rust values being a usable language too much to be 100% sound.

I don't understand why you and others think that bringing up existing and irritating soundness holes in Rust is an argument for introducing new ones. Whether or not you think CXX exposes a sound API, it's a bad argument. This one is literally a bug that we can hopefully fix at some point, just like the floating point casts one was finally fixed.

#[no_mangle], like many other Rust features, is unsound; that's a fact and not an opinion, there is a bug in the tracker open for it and labelled I-unsound (accepted as an unsound bug). It is a low priority issue, but will be fixed eventually.

> Frankly Rust values being a usable language too much to be 100% sound.

Rust has a really good track record of identifying, prioritizing, and fixing soundness bugs (e.g. it took 4 years of work to fix one floating-point soundness bug!). Many people continuously work on this at the academic, toolchain, and backend (llvm, crane lift) levels. There is also a lot of people continuously working on making sure that new language features like async/await, const generics, specialization, GATs, ... are sound.

TBH i'm surprised to learn that not all Rust core members consider soundness to be a Rust core value. Maybe it isn't a Rust core value? (it is for me; without it, Rust makes no sense as a language to me)

I think that’s a fantastic compromise. I am 100% in agreement with you on this topic, FWIW.

How is that a compromise?

The unsafe in `unsafe mod ffi { ... }` is literally the proof that all APIs exposed in the block are sound to call from safe Rust.

It would only need to go hand in hand with a comment explaining why each API in the block is sound to call safely, and it allows users to not list unsound APIs in there, but wrap them manually when needed. Along with code that checks the C++ lib version, etc. to make sure that the proof are kept in sync with each version of the lib.

That's completely different from not requiring any unsafe in the Rust side, and doing this en masse via bindgen.

> How is that a compromise?

How is it not a compromise? Party A wanted (and implemented) something that didn't require `unsafe`. Party B wanted `unsafe` to be used. After much discussion, Party A concedes with allowing people to put `unsafe` in some of the code, and Party B concedes that putting it there is sufficient instead of requiring it to be strewn all over the place.

Sounds like a (reasonable) compromise to me.

Can you explain why having to put "unsafe" before FFI calls is considered a problem? That's the part I don't get, isn't the whole point of using Rust that you want sound, memory-safe code by default?

If you don't want to have unsafe everywhere it seems to me that the reasonable path (taken by many Rust libraries) is simply to wrap the raw C/C++ interface around a Rust interface that enforces the safety invariants. That's why you can write safe GTK or OpenGL code in Rust, using Rust libraries that expose an actually sound interface.

I mean, FFI code is tagged as unsafe because it is unsafe to call. I don't see how "but I don't like having to write unsafe everywhere in my FFI code :(" is in any way a reasonable technical argument here.

> Can you explain why having to put "unsafe" before FFI calls is considered a problem?

I intentionally avoided taking a side in this debate. My response was focused on the claim that there was no compromise between the parties, when I think it's pretty clear cut that there is a compromise.

Personally, I'm sympathetic to both parties. In my own personal FFI-binding library I chose to let the programmer decide per-method whether it's safe or unsafe.

I'm all for compromise but first I need to understand the pros and cons of both sides. For me here so far it's pretty clear cut: there's a right way and a wrong way. The compromise is just a slightly less wrong way. So no I don't accept this compromise in this situation.

Some coders want their code to be safe. Some coders want their code to look safe. Feel the difference.

Rust requires all safe Rust code to not have undefined behavior.

Party A wanting safe Rust to have undefined behavior was wrong. Party A now adds unsafe to their API, so that undefined behavior only happens in unsafe Rust.

I don't see the compromise anywhere. Party B told party A that they were wrong, and party A acknowledge it and fixed their crate.

You either haven't looked at how cxx works, or you're intentionally misrepresenting it.

cxx generates code that uses `unsafe` blocks/functions. It then generates safe wrappers around those, which are what it exposes to users. It's no different then someone doing this:

  pub fn safe_fn() {
    extern { fn unsafe_fn(); }
    unsafe { unsafe_fn(); }
cxx just uses macros to generate that. You're welcome to have a different opinion on whether or not the user must pass an `unsafe` token to the macro. But that has no bearing on the code generated by the macro.

Statements like "Party A wanting safe Rust to have undefined behavior was wrong" are just straight up incorrect.

Someone doing what you just described is wrong in the context of C++, at least without manual review.

I know how cxx works, and I am not misrepresenting anything.

cxx is currently unsound, and the author (dtolnay) has chimed in above with a fix they are considering that fixes it (https://news.ycombinator.com/item?id=24244121)

The API of the cxx crate is safe, and it can cause UB, therefore it is unsound.

It doesn't matter that the cxx crate API is a macro. Yes, this macro expands to unsafe code like you mention, but the problem is that this unsafe code is often "broken" (unsound).

Unsafe != unsound

I know.

The Rust spec defines "unsound" as "introducing undefined behavior in safe Rust". The API of the cxx crate allows a safe Rust program to have undefined behavior, and it is therefore unsound.

Rust requires ALL CODE to not to have undefined behavior. Unsafe doesn't mean you can do whatever you want it just means that you are guaranteeing to the compiler that this block is safe.

> The unsafe in `unsafe mod ffi { ... }` is literally the proof that all APIs exposed in the block are sound to call from safe Rust.

I think there might be a misunderstanding here. I interpreted the `unsafe mod ffi { ... }` to be like `unsafe fn foo()`, declaring the module as unsafe, not an unsafe block where we're telling the compiler we will maintain the invariants ourselves.

It is somewhat unfortunate both the proof obligation and proof 'declaration' use the same token.

> It is somewhat unfortunate both the proof obligation and proof 'declaration' use the same token.

There have been some RFCs open to improve this situation (e.g. unsafe blocks in unsafe functions comes to mind).

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact