Remix.run Logo
BearOso a day ago

> Rust does this automatically.

A garbage collected language does this automatically. Rust still requires thinking about and tracking memory lifecycles, but the borrow checker will complain and keep you from doing it wrong. That's why LLMs like Rust. It gives immediate feedback on what to fix. By-default constant reference parameters helps prevent major performance problems.

Diggsey 15 hours ago | parent | next [-]

You're getting confused between lifetimes (the static analysis that prevents use after free and similar errors) and lifecycles (more commonly discussed under the heading of ownership) which determines when objects (and thus memory) are allocated and deallocated.

Ownership is automatic. You don't have to explicitly drop things when they go out of scope. (although the responsibility for that is split between the compiler and the library code).

Lifetimes are not automatic, but also have no effect on memory allocation. They are purely a static analysis path and you can make a functioning rust compiler that completely ignores lifetimes.

soulbadguy 14 hours ago | parent [-]

> You're getting confused between lifetimes (the static analysis that prevents use after free and similar errors) and lifecycles (more commonly discussed under the heading of ownership) which determines when objects (and thus memory) are allocated and deallocated. Ownership is

That's a semantic distinction which does not matter in the point OP is making. And contrasting rust static approach to general GC.

gcr 15 hours ago | parent | prev | next [-]

It’s my understanding that bun was ported to unsafe rust, so even these gains would require additional effort on the team’s part, right?

Ygg2 13 hours ago | parent [-]

Unsafe Rust doesn't automagically disable typesystem (& borrow checker, but lifetime are a sort of types).

Once raw pointer is turned into a T, &T or &mut T, the borrow checker is on.

adwn 12 hours ago | parent | next [-]

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.

Ygg2 11 hours ago | parent [-]

> True, but unsafe let's you conjure up any lifetime you want

The only thing unsafe does is let you have an unbounded lifetime. As I said, it doesn't check those:

   fn get_str<'a>(s: *const String) -> &'a str {
       unsafe { &*s }
   }

https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html

> if you generously sprinkle pointer dereferences in unsafe code, you effectively disable the protection provided by the borrow checker

You don't disable anything. You wrote a "trust me compiler" block, and compiler trusted you.

Rust won't ever protect from all possible problems, just the ones the compiler handles.

geon 4 hours ago | parent | next [-]

> You don't disable anything. You wrote a "trust me compiler" block, and compiler trusted you.

That’s the same thing.

zombot 10 hours ago | parent | prev | next [-]

What's the point of using Rust in the first place when you disable the compiler feature that protects you the most?

dkersten 3 hours ago | parent | next [-]

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.

That’s a very useful property to have.

imtringued 5 hours ago | parent | prev | next [-]

Well, Rust has things like miri that can help you write your unsafe code and it's just a command line invocation away.

Obviously you should try to avoid writing unsafe Rust to begin with.

Ygg2 9 hours ago | parent | prev [-]

Mu. Invalid question. Rust doesn't disable compiler features. It gives you extra set of footguns when you ask for it.

As for the actual unloaded question, "What's the point of unsafe in Rust?" it is to contain and make it easier to identify sources of UB.

gcr 6 hours ago | parent [-]

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.

whytevuhuni 6 hours ago | parent [-]

Only ~4% of code is inside an unsafe block, so the idea is that for new code/contributions, the chance of introducing a new memory-safety bug is an order of magnitude lower.

Maybe in the future the unsafe code will go down to 1%, bringing that to two orders of magnitude.

Of course, only time will tell if that is true or not, but from experience I’d be willing to bet it is.

adwn 6 hours ago | parent | prev [-]

> The only thing unsafe does is let you have an unbounded lifetime.

No, you're wrong: You can create any lifetime. Proof:

    fn oof<'desired>(x: &u32) -> &'desired u32 {
        let ptr = x as *const u32;
        unsafe { &*ptr }
    }
This will take a reference and return it with any lifetime specified by the caller.

> You don't disable anything.

I said "effectively disable". For example:

fn trust_me_bro<'a>(x: mut u32) -> &'a mut u32 { unsafe { &mut x } }

    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.
Ygg2 5 hours ago | parent [-]

> Proof:

You just listed examples of unbounded lifetimes.

> I said "effectively disable". For example:

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).

What you probably meant is https://play.rust-lang.org/?version=stable&mode=debug&editio...

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.

adwn 3 hours ago | parent [-]

> You just listed examples of unbounded lifetimes.

You're just splitting hairs and trying to weasel around the fact that yes, you really can create any lifetime you want using unsafe.

> Not sure what you meant by this example since it doesn't compile.

That's because the crappy HN formatting ate some of the characters. Here's the original version:

https://play.rust-lang.org/?version=stable&mode=debug&editio...

> What you probably meant is […]

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.

imtringued 5 hours ago | parent | prev | next [-]

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...

zombot 10 hours ago | parent | prev [-]

Borrow-checking the dereference of a stale pointer won't be worth much, though.

josephg 8 hours ago | parent [-]

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.

gcr 6 hours ago | parent | next [-]

That’s my whole point! It’s ported to unsafe rust, not default rust.

zombot 6 hours ago | parent | prev [-]

The point of compiler-based checking would be that you know for sure. If you have to bet, all bets are off.

smolder 9 hours ago | parent | prev [-]

You don't need to rehash the same old argument. Runtime memory management is forgiving but also inferior in a lot of ways to compiled stuff that works with memory deterministically.

Garbage collection is a method to make programming easier, not to make the resulting program better.