Hacker News new | ask | show | jobs
by charcircuit 898 days ago
I'm skeptical that this is better than starting to rewrite the kernel in Rust. It would be better if everyone can focus on rewriting things into Rust rather than having a choice of rewriting into C++ or Rust. Unlike this email, C can be converted into Rust piecemeal and integrate with the rest of the kernel. It's also in kernel developers best interest to start learning Rust, and getting them to start earlier than later will be beneficial.
3 comments

I'm skeptical because C++ is a slippery slope. Glad to see they are going for templates and concepts, and not constructors and destructors.

Yes, what C++ is supposedly good for – RAII, it actually got a little wrong:

1. Default construction / value initialization: Causes lots of initialization before assignment that is obviously unnecessary. Try allocating a buffer: `std::make_unique<char[]>` surprisingly memsets the buffer.

2. Constructors: No way to fail without exceptions. That buffer example again: Without exceptions, `std::make_unique<char[]>` will actually attempt to memset a nullptr on allocation failure … before your nullptr check (which btw makes the compiler entitled to delete your nullptr check as well).

3. Move is a (burdensome) runtime state that mandates nullability.

4. Destructors: Can't take arguments, forcing objects to contain implicit references to each other.

Rust's affine type system fixes 1-3, but not 4, which only a linear type system could: https://en.wikipedia.org/wiki/Substructural_type_system#The_...

> 4. Destructors: Can't take arguments, forcing objects to contain implicit references to each other.

This issue has been mentioned back in 2018: https://lore.kernel.org/lkml/CAOMGZ=HwTjk9JbGNeWHd+Jr3rwOkFO...

> I'm skeptical that this is better than starting to rewrite the kernel in Rust.

I recently migrated a complex cross platform C99 embedded project to C++17. We got the build systems ported in a day and are able to chip away at it piece by piece.

We looked at Rust but the main issue wasn't technical it was the large learning curve for the team. Rust is not as intuitive to C developers are C++ is.

> Unlike this email, C can be converted into Rust piecemeal and integrate with the rest of the kernel.

Did you read the entire email? Here's a quote that seems to directly contradict you:

> converting C code to Rust isn't something that can be done piecemeal, whereas with some cleanups the existing C code can be compiled as C++.

As far as I can tell, the author is correct. What is the pattern for converting C to Rust piecemeal?

> What is the pattern for converting C to Rust piecemeal?

You can call Rust from C, and C from rust. You can convert it function by function.

Won’t you end up with mostly _unsafe_ code this way?
It is true that the boundary will be unsafe, and that's more than if it were to be all in pure Rust, but that does not change that, even when doing kernel level work, in practice it has generally been shown that unsafe code is still relatively small in amount.
It really depends. If the interface that you're wrapping can have a nice, abstract API (like "parse this JPEG header into a struct" or something), then the hope is that you can figure out how to express that API in safe Rust, even though underneath it might be a big pile of C. The trouble is when there is no clean API boundary that can fit within safe Rust's rules, like when the underlying C code is "object soup" where everything has pointers to everything else, and it's all up to the programmer to know when it's safe to free things. In that case trying to wrap all that in Rust either gives you unsafe code everywhere, or maybe worse, "safe" APIs that are lying to you and are actually unsound.
> then the hope is that you can figure out how to express that API in safe Rust, even though underneath it might be a big pile of C.

This is actually not what you want, because as a rule Safe Rust enforces the use of Rust references which have stricter requirements than C++ references or C/C++/Rust raw pointers, and will otherwise introduce UB. (See the Rustonomicon for a detailed description of those requirements.) A "safe Rust" API is OK when all callers can be proven to satisfy these requirements, otherwise raw pointers are easier even though they must be accessed in an unsafe block.

There are also pitfalls when passing arguments by value to Safe Rust, e.g. a `bool` value MUST be 0 or 1, an `enum` MUST not have an invalid discriminant etc. Breaking any of these requirements when calling Safe Rust from C/C++ makes instant UB a very real possibility.

What you're talking about is what I was referring to as "safe APIs that are lying to you and are actually unsound". I've written more about safety and soundness here: https://jacko.io/safety_and_soundness.html
I hope I am wrong, but this sounds as if the guy wrote the email thought that C is a subset of C++.

No it is not. First example: initialisation of structs

>What is the pattern for converting C to Rust piecemeal?

You take a piece and either rewrite it yourself or use a transpiler that produces potentially unsafe Rust code which you then later would want to rewrite into safe Rust code.

A recent practical example of the former: the fish shell re-wrote incrementally from C++ to Rust, and is almost finished https://github.com/fish-shell/fish-shell/discussions/10123

An example of the latter: c2rust, which is a work in progress but is very impressive https://github.com/immunant/c2rust

It currently translates into unsafe Rust, but the strategy is to separate the "compile C to unsafe Rust" steps and the "compile unsafe Rust to safe Rust" steps. As I see it, as it makes the overall task simpler, allows for more user freedom, and makes the latter potentially useful even for non-transpiled code. https://immunant.com/blog/2023/03/lifting/