Posted by ravenical 11 hours ago
https://www.reddit.com/r/rust/comments/1q0kvn1/corroded_upda...
As a follow on to the corroded meme crate:
https://github.com/buyukakyuz/corroded
> What Is This
> The rust compiler thinks it knows better than you. It won't let you have two pointers to the same thing. It treats you like a mass of incompetence that can't be trusted with a pointer.
> We fix that.
fn main() {
let a = String::from("hello");
let b = a;
println!("{a}"); // Works! Prints: hello
}
This is not “I have correct code but Rust can’t tell it’s correct.” This is “wow, this code is intentionally outrageously wrong, obviously dereferences a pointer that is invalid, and happens to work anyway.”Can you explain why? Why can't both a and b point at the same string object? Does `let b = a;` do something like a destructive move?
In rust you don’t have a garbage collector and you don’t manually deallocate - if the compiler is not certain of who drops memory and when, what happens with those ambiguous drops ?
In other words, are the silenced errors guaranteed to be memory leaks/use after frees?
Languages like C compile code with the understanding that if the compiler can't prove the code is incorrect, it'll assume it's correct. Rust compiles with the expectation that unless the compiler can prove the code correct (according to the language rules), it won't compile it. In C, all programs that only perform defined behaviour are valid, but many programs which exhibit undefined behaviour are also valid. In safe Rust, all programs which exhibit undefined behaviour are invalid. But as a trade-off, many programs which would actually execute perfectly well are also considered invalid.
In both cases, once you get past the layers that check stuff, you may normally assume that whatever you have has already been shown to be OK and you probably don't have enough information to re-check while compiling. It might blow up at runtime, it might not.
The final program may be broken in various manners because you don't respect the language's prescribed semantics, in about the same way they do in C and C++. From the compiler's perspective the borrow checker validates that rules it assumes are upheld are actually upheld.
mrustc already compiles rust code without having a borrow checker (well IIRC recent-ish versions of mrustc have some borrow checking bits, but for the most part it still assumes that somebody else has done all the borrow checking).
It doesn't actually depend on the borrow checker. All lifetime labels are discarded after being checked. Code generation has no idea about borrow checking. Once the code is checked, it is compiled just like C or C++ would, just assuming the code is valid and doesn't use dangling pointers.
Borrow checker doesn't affect program behavior. It either stops compilation or does nothing at all. It's like an external static analysis tool.
One example might be a tree-like struct where a parent and child have references to each other. Even if everything is cleaned up properly, the borrow checker has no way to know that when the struct is created. Solving it requires unsafe at some point, usually through something like RefCell.
No, not at all. The examples at the beginning of the article show this - they'll execute correctly. The borrow checker is quite conservative, and rules out all sorts of code that won't (normally!) cause runtime errors.
It's fairly easy to see this if you think about the core of Rust's ownership model: every value in Rust has a single owner. The compiler enforces that for any value, there's either one mutable reference or any number of immutable references to it at a time.
This model has the advantage of being simple, easy to reason about, and ruling out large classes of errors. But like most static checks, including e.g. traditional type checks, it also rules out a great deal of otherwise valid code.
It's easy to think of examples in which you have multiple mutable references to a value that won't cause errors. Aside from trivial examples like in the article, in C-like languages you can have many concurrent mutable references to the same mutable value. You can safely (with some caveats) manage access to it via locks, protocols, documentation, or just being careful. Rust with the borrow checker simply doesn't allow multiple concurrent mutable references to the same value to exist. Rust without the borrow checker, as in the article, would allow this.
I think fighting the borrow checker is more like a rite of passage. Rust is not my favorite language but the borrow checker is great.
The two main things the compiler allows in an unsafe block but not elsewhere are calling other code marked "unsafe" and dereferencing raw pointers. The net result is that safe code running in a system that's not exhibiting undefined behaviour is defined to continue to not exhibit undefined behaviour, but the compiler is unable in general to prove that an unsafe block won't trigger undefined behaviour.
You can side-step the borrow checker by using pointers instead of references, but using that power to construct an invalid reference is undefined behaviour.