> Refactoring can be painful in Rust when it involves very low-level changes in semantics
I don't know what this is about. In my experience, refactorings that change the semantics of APIs are much easier in Rust than in C. E.g., change assumptions about the lifetimes of pointers passed into APIs: the Rust compiler will tell you where you need to change anything; the C compiler will happily compile your code and you'll corrupt memory at runtime.
Right, and while sometimes a lot of code needs to change in Rust, it's often largely mechanical, at least in typical contexts (e.g. user land development). But Rust won't automatically detect changes in semantics that happen across FFI boundaries. And I didn't argue that refactoring is easier in C than Rust. For one thing, it's an inapt comparison in the context of a mixed C and Rust kernel codebase in which the vast majority of the code and semantics come from the C side.
And there are more subtle issues. From the perspective of C code, Rust looks and behave as if it assumes strict aliasing, a consequence of the borrowing rules--a mutable pointer can never alias. But the Linux kernel uses -fno-strict-alias (i.e. any pointer can alias, regardless of type), so a subtle change in C code which works fine for the kernel could silently break Rust code if the C-side developer wasn't aware of these subtle nuances in memory models and how they were expressed in the unsafe wrappers. This might be a totally contrived scenario[1], or it could be very real given the tower of abstractions built on the Rust side over the C APIs, which might overspecify certain semantics to fit "cleanly" (i.e. best practice) into the Rust model.
Which points at another issue: all of these hypotheticals might be (probably are?) overblown. But over the past 2-3 years, with the exception of a couple of high-profile drivers not yet mainlined (AFAIU), the vast majority of the effort on the Rust side has been building towers of abstraction. In almost any open source project, C or otherwise, a newcomer who starts writing towers of abstractions rather than concrete, usable code would be shooed away. There are reasonable justifications for why Rust has been doing this, yet its also understandable why this might draw suspicions about the practicality and utility of mixing C and Rust in the kernel. But either way it means that after all this time people are still largely arguing over hypotheticals.
[1] Or entirely wrong. Maybe kernel Rust is using flags to ensure aliasing optimizations can't happen. clang (and thus LLVM) supports -fno-strict-alias, but AFAIU alot of Rust code massaging happens on the Rust (MIR or equivalent) side.
I know Rust doesn't do "strict aliasing", literally speaking. But that's partly because Rust's borrowing rules change the framing of the term strict aliasing as used in C, etc. It's similar for terms like "undefined behavior", where there's sometimes a certain amount of pedanticism from Rust developers that merely serves to deflect the point of contention.
> In the previous example, we used the fact that &mut u32 can't be aliased to prove that writes to output can't possibly affect input. This lets us cache *input in a register, eliminating a read.
Yes, it is a completely different set of rules. Strict aliasing is also referred to as type based aliasing analysis. Rust has categorically rejected this form of analysis, the aliasing rules are very different. It has nothing to do with types.
Oh, and "undefined behavior" has effectively the same definition in Rust and in C and C++. There's wording differences:
C/C++: behavior for which this document imposes no requirements
Rust: is not bound by any specification
But that's just a wording difference, not a semantic difference.
There are some instances where Rust and C/C++ use similar words to mean different things, but that's also changed over time. For example, Rust used to use rvalue and lvalue, but has moved to
"place expression", defined as "lvalue" in C and "glvalue" in C++
"value expression", defined as "rvalue" in C and "prvalue" in C++
The only reason to not use the C++ terms here is that these are the only two value categories in Rust, and it's unlikely to need all of the other three that C++ has.
You're not wrong that there's stuff called the same but has different meanings, for example, "reference," but what can you do.
I don't know what this is about. In my experience, refactorings that change the semantics of APIs are much easier in Rust than in C. E.g., change assumptions about the lifetimes of pointers passed into APIs: the Rust compiler will tell you where you need to change anything; the C compiler will happily compile your code and you'll corrupt memory at runtime.