Learn Rust the Dangerous Way
cliffle.com
cliffle.com
Behind the scenes, write a short C program, and compile it into LLVM IR through Clang. Present the code first as its LLVM IR representation, then work through how you’d manually decompile it back into C, hopefully giving a result that will compile back to the same LLVM IR.
(You’d be teaching some of the fundamentals of compiler theory here, in “reverse order”, but IMHO that’s exactly what you need if you’re to understand where C and Rust really diverge.)
Then, walk through the decompilation process again, except this time with Rust semantics, to end up with unsafe Rust that compiles to identical LLVM IR as the C code does.
And then, finally, treating the unsafe Rust as the new IR, walk through how you’d decompile that unsafe Rust into safe Rust, introducing each static-analysis compiler pass that Rust does in reverse, adding one feature at a time, until the code is all safe Rust, but can be theoretically partial-compiled into the earlier unsafe-Rust code†, and from there into the same LLVM IR as the other two examples.
The idea here is that this tutorial would be a way of “reading history backwards” (ala https://slatestarcodex.com/2013/04/11/read-history-of-philos...). I.e., rather than coming into a world with a fully-formed Rust and a fully-formed C, you’d start with a world that only has ASM; and then introduce C into it; and then introduce Rust into it, seeing how each introduction changes what you can do.
† It’d be helpful if rustc had a mode where it could emit some sort of “core Rust” IR, that was all unsafe{} and had only the barest types, in the vein of early C++ compilers that emit C, or how the Erlang compiler works. Sadly, I don’t think rustc is architected in a way that’d allow this. (Though, paging steveklabnik to correct me.)
In general, moving from one language to a "higher level" or "stricter one" (such as unsafe Rust to safe Rust) requires a redesign to properly do it (if at all possible).
Rust has a "core Rust" called MIR. It has nothing to do with unsafe and definitely doesn't remove types though. It's mostly about desugaring constructs, so by and large a simplified version of Rust (though it has things normal Rust doesn't, namely goto).
Alternatively, mrustc compiles Rust via C.
MIR seems to be the closest thing to a "Core Rust". You still have access to all the types, but everything is desugared down to the barest possible syntax. While and for loops are desuggared to loop+break, complex field accesses are split into temporary to ease borrow analysis, etc...
I'm not sure what happens to unsafe block, but I suspect those gets stripped as well, and MIR is assumed to be valid. There's a tool called miri that can be used to find UB by running the MIR - if you manage to trigger UB, miri will error out telling you what went wrong.
Might be worth looking into it :D
How were you able to see it?