True, but unsafe let's you conjure up any lifetime you want, or any lifetime necessary to satisfy the lifetime requirements in safe code. If you generously sprinkle pointer dereferences in unsafe code, you effectively disable the protection provided by the borrow checker – including in safe code – until you've checked and verified the correctness of all unsafe blocks.
Because it lets you constrain the parts that the compiler can’t check and has to trust you on. The alternative is either a langue that can’t do necessary things, or a language that can’t check what could be checked.
With unsafe, you’re telling the compiler “I’ve taken extra care to make sure that what I’m doing is safe and doesn’t break your rules” and the compiler can go ahead and assume that you don’t, in fact, break the rules, and therefore can verify everything else as if the rules never got broken.
In less safe languages, the entire program is “trust me, it’s safe”, while in rust only the parts flagged as unsafe are.
The point is that you should only use unsafe when 1. It’s absolutely necessary for functionality or performance and 2. You have verified and are very certain that the code is correct.
Yes, when a human does it. With an LLM, the "trust me, I took extra care" is extremely doubtful. If LLMs could do that, they might as well use unsafe languages.
The whole point of the rust rewrite is that bun is now thought to rely on rust’s memory safety features, but that assumption doesn’t hold if everything’s inside an unsafe block.
fn main() {
let mut x = 1_u32;
let reference_a = &mut x;
let reference_b = trust_me_bro(reference_a);
*reference_b = 2; -- Whoops
println!("reference_a: {reference_a} reference_b: {reference_b}");
}
After the call to trust_me_bro, two aliasing, mutable references exist simultaneously. This would usually be prevented by the borrow checker, but the unsafe code has effectively disabled it.
Not sure what you meant by this example since it doesn't compile. It seems the borrow checker caught your mischief. So much for effectively disabling stuff :P
You haven't effectively disabled anything; you just (tried to) wrote unsound code that washes one mutable ref as another. This stuff is allowed provided shared refs are never accessed at the same time (for example, panicking upon reading reference_b).
But you know what? If you're dabbling in unsafe, you have this big button called Tools in the playground. Choose Miri, then run your code; it will display large Undefined behavior. It even highlights the `trust_me_bro` function.
Hell, run this with UBSAN, ASAN, and other C tools. They will probably catch any such behavior.
If you know what I meant, then what's up with your snarky comment about "So much for effectively disabling stuff"? In your playground link, you did effectively disable the borrow checker in safe parts of the code. Next thing you're moving the goalpost, now it's not about external, static analysis tools: "run this with UBSAN, ASAN, and other C tools". I don't think you're arguing in good faith here. Goodbye.
Yeah but if you have tried writing unsafe Rust, you notice that you are obligating yourself to write code that is much stricter than C.
E.g. in C you can write code and say "don't call it outside the situation that this function was written for" and then blame [0] future users of the API.
In Rust you need to actually make sure that your code works, i.e. your safe interface is not unsafe.
[0] The blame game is not a technical solution to a technical problem...
Sure; but I bet raw pointers are used very infrequently in the new codebase. The code was ported from zig to rust. I bet a lot of pointers became rust references in the process.
Once raw pointer is turned into a T, &T or &mut T, the borrow checker is on.