| ▲ | mrothroc a day ago |
| Drilling into the original article where Jarred explained the reasoning behind the change, It's pretty clear that under zig the team was doing things by hand that are automatic in rust. Humans and agents share one thing: they are both non-deterministic. He talks about the issue of tracking memory lifecycles manually in zig so it can be explicitly freed. As expected, this leads to a long list of bugs where people missed things. Rust does this automatically. It removes an entire class of errors from his backlog. From an engineering management perspective, this looks like a pretty good trade. The bonus here is that compiler errors are exactly the kind of deterministic guardrail you need to put around coding agents. Claude works really well if you give it a way to test for correctness and "make it compile" is a pretty good target. There's a general version of this: the artifact you expose plus the test you run on it. Deterministic tests turn stochastic output into a hard guarantee. Wrote it up here if useful: https://michael.roth.rocks/blog/verification-surface/ |
|
| ▲ | awesan a day ago | parent | next [-] |
| Zig (like C) is simply not a good language to use if you're going to do many small allocations with uncorrelated lifetimes. To write robust Zig (or C) code, you must manage lifetimes yourself, for example by grouping allocations on an arena or by having fixed buffers of "things". You can just do that, and then Zig is really no less robust than Rust. But if you want to do "managed language" style allocation patterns (like what llms generally prefer), it doesn't make sense to use it. |
| |
| ▲ | Jarred 9 hours ago | parent | next [-] | | Grouped and arena allocations work really well in Zig. For awhile, we tried to use this pattern almost everywhere in Bun but it gets really tricky when there’s some GC-managed memory and you want to free things incrementally to reduce RSS. Also, using arenas for arrays that grow wastes memory a lot since it keeps every previous version around (mimalloc arenas are slightly better for this) Grouped allocations works especially well in parsers & ASTs where the lifetime is very bounded. Since the Rust rewrite, we still use arenas for Bun’s parsers and the bundler but not a ton elsewhere. | | | |
| ▲ | kllrnohj 15 hours ago | parent | prev | next [-] | | > You can just do that, and then Zig is really no less robust than Rust. If you just don't write bugs, then yes all languages are equally robust, including assembly. Zig, like C, is simply not a robust language. I don't know why this feels like something contentious? It's clearly not intended to be robust? | | |
| ▲ | audunw 14 hours ago | parent | next [-] | | Zig is intended to be as robust as it can be as long as it doesn’t implicitly add code (no destructors that run code you didn’t explicitly call), or increase compiler complexity and compilation time. I don’t think it makes sense to say Zig is or isn’t intended to be robust in general. Like, we don’t say Rust isn’t robust since it doesn’t add dependent types and general purpose static verification that can do more general proofs. It’s focused on eliminating one class of memory bugs in particular, exactly the class of bugs that are the biggest challenge for software like Bun, and other software with complex lifetimes (it originated from Mozilla and Rust is perfect for browsers) Zig is intended to be robust for software like TigerBeetle, or the Zig compiler itself, where memory lifetimes are simple. I’d say the focus on built in tests, fuzzing, debug memory allocators and safe mode shows that Zig is absolutely intended to be robust, within the scope of what the language aims to be. Far more than C itself or most of its popular compilers ever did. | | |
| ▲ | KingMob 13 hours ago | parent | next [-] | | A casual read of TigerBeetle's practices makes it clear they're doing some very unusual things, both in their memory allocation strategy and in their testing/verification. Despite TigerBeetle being one of the highest-profile remaining Zig projects, I actually don't think they're representative of the average Zig project at all. | | |
| ▲ | GuB-42 5 hours ago | parent | next [-] | | It is quite representative of an embedded software project. TigerBeetle is not what we usually call embedded software, but it is built like it: static memory allocation, a self-contained executable, and a strong focus on determinism are typical in that field, especially for critical software. And I think embedded software is a field where Zig will be at its best. The only thing it is missing is maturity. When project lifetimes are measured in decades and changing a single byte can cost millions, no one in his right mind will pick a language that is still in development. Things will become interesting when it reaches 1.0. | |
| ▲ | jandrewrogers 3 hours ago | parent | prev | next [-] | | Static memory allocation is idiomatic in high-performance software. You do the same in C++ if you care about performance and reliability. | |
| ▲ | Yokohiii 12 hours ago | parent | prev [-] | | Can you elaborate on the unusual part? | | |
| ▲ | WD-42 12 hours ago | parent [-] | | All necessary memory is allocated at initialization. The application is not allowed any allocations during it's normal runtime. This is how it avoid memory bugs. Not a lot of people write programs this way. | | |
| ▲ | physicsguy 10 hours ago | parent | next [-] | | Static memory allocation is widely used in quite a bit of embedded software, particularly safety critical stuff. There it's often latency related since you don't want hangs while memory is allocated. I've even seen it on some simulation software's core that was written in the 80s originally; at the time memory was much more constrained so allocating upfront meant you could check upfront whether the simulation could actually run or not vs crashing out part way through. | | | |
| ▲ | ahoka 11 hours ago | parent | prev | next [-] | | A lot of people write software like this when predictable latency is a hard requirement. | | |
| ▲ | leonidasrup 2 hours ago | parent [-] | | Andrew Kelleys original motivation for creating Zig was language that allowed precise low-level control for real-time audio processing. "Kelley describes why he created Zig, when other options including C, C++, Rust, and Go already exist. He said he set out to develop a digital audio workstation. He tried Go, but found interoperability with C libraries difficult, and said the garbage collector caused audio delays. He tried C++, or coding C-style using a C++ compiler, but found that small mistakes led to memory corruption bugs that took weeks to fix. He tried Rust but "really struggled to write code that would satisfy Rust's rules," and spent a month trying to make font rendering work." https://www.theregister.com/software/2026/05/28/zig-creator-... |
| |
| ▲ | n6242 11 hours ago | parent | prev | next [-] | | I was just learning yesterday that's exactly what GTA did on PS2, which I thought was interesting. (Not to belittle your point, just giving an example) | | | |
| ▲ | 11 hours ago | parent | prev | next [-] | | [deleted] | |
| ▲ | NothingAboutAny 11 hours ago | parent | prev | next [-] | | I do this in Unity because allocations/GC cause hitches.
it's pretty normal thing to do there's even the built in pooling libraries so you can pre-allocate 10,000 gameobjects when the game starts.
I haven't played with ECS/DOTS yet but I assume it does something similar. | |
| ▲ | pton_xd 11 hours ago | parent | prev | next [-] | | This is standard practice in the games industry. | |
| ▲ | tovej 5 hours ago | parent | prev [-] | | I do. The only thing I need dynamic allocations for is queues of asynchronous events, and that's just because I'm too lazy to calculate an upper bound for how many there may be. |
|
|
| |
| ▲ | eru 14 hours ago | parent | prev [-] | | > Like, we don’t say Rust isn’t robust since it doesn’t add dependent types and general purpose static verification that can do more general proofs. Give it a few years! I've noticed an explosion in interest in formal verification recently, especially since nowadays the bar to entry is so low: just ask your LLM agent to give it a go. |
| |
| ▲ | awesan 6 hours ago | parent | prev | next [-] | | Realistically much of the most reliable software in the world was written in C. Robustness is more so a function of coding style and engineering practice than it is of the programming language chosen. Of course you could argue that on average, most programmers are not going to have the right practices and skill, so on average you should prefer Rust. But that's unrelated to the argument I was making, and in any case not a very interesting point in my opinion. | | |
| ▲ | xedrac 4 hours ago | parent | next [-] | | Conversely, most of the high impact bugs are also written in C and C++, because they rely on "coding style and engineering practice" to be correct. Rust raises the floor on this by a lot. | |
| ▲ | jstimpfle 6 hours ago | parent | prev [-] | | Adding that with a principled approach, I don't even see much of an issue with doing manual creates and deletes in a object-graph type app, with many unstructured lifetimes. Sometimes that might just be required, and then the complexity is just there either way. Having to cleanups manually or not doesn't change anything about that. It's a bit more cumbersome to get everything right when doing it manually -- sure. The problem is mostly people graduating from school thinking that somehow there is only stack and heap, and malloc/free is how you do heap. That view completely ignores that the essence of programming systems is mostly to understand the machine, and then doing conceptual and architectural work on a solution (and also on a problem). The act of writing actual code is then mostly just translating those concepts into the digital world verbatim. |
| |
| ▲ | jackclayton 14 hours ago | parent | prev | next [-] | | Above poster is talking about thinking in terms of grouped lifetimes and bulk allocations/deallocations, which is better for performance, and makes Rust borrow checking and other RAII style features pointless as they don't add any safety benefits. This video completely changed the way I think, and I subsequently moved on from Rust: https://www.youtube.com/watch?v=xt1KNDmOYqA | |
| ▲ | relug 13 hours ago | parent | prev | next [-] | | the buffer managemnt is just different pattern and style of code thats more low level. when you care about performance and cpu cache, you have to make sure that actual physical memory gets computed at same time as other memory near it so there is less latency. | | |
| ▲ | Barrin92 13 hours ago | parent [-] | | the primary motivation isn't latency but complexity. People do in some applications free or allocate collectively because they have interrupt times in mind, but most of the time when you manually manage memory the issue is mental overhead, so people gravitate towards models they can keep in their head. Allocating in large chunks is often not very performant which is why people came up with tools like the borrow checker, you often want to allocate and deallocate dynamically on a need-basis but that's exactly where bugs occur. |
| |
| ▲ | 6P58r3MXJSLi 5 hours ago | parent | prev [-] | | > Zig, like C, is simply not a robust language "Extraordinary claims require extraordinary evidence" -- Carl Sagan |
| |
| ▲ | hresvelgr 10 hours ago | parent | prev | next [-] | | > You can just do that, and then Zig is really no less robust than Rust. That's just it, using Zig required more rigorous engineering than the Bun team were capable of. | | |
| ▲ | rob74 9 hours ago | parent | next [-] | | People tried to "just do that" (write their programs in a memory safe way) in C and other languages with manual memory management for decades, and we have countless vulnerabilities that prove this simply doesn't work. That's why newer languages, with the notable exception of Zig, prefer more advanced memory management methods. | | |
| ▲ | hresvelgr 6 hours ago | parent [-] | | > People tried to "just do that" (write their programs in a memory safe way) in C and other languages with manual memory management for decades, and we have countless vulnerabilities that prove this simply doesn't work. Who are these "people" you speak of? It's possible to write software in low level languages that don't have these problems. Not a "non-zero" it might be possible, it can be done thoughtfully, and the popular notion it can't be done is backed only by incomplete anecdotes. Should everything be written in low-level languages? No, that would be absurd. Is it a simple fact of life that not every person/team/organisation is capable of meeting certain standards of rigour? Yes. That's not to say anyone in the Bun team could not become sufficiently competent in the future. For whatever reason, current experience, incentives, and personal motivations did not make for a conducive environment to make Bun watertight in Zig. | | |
| ▲ | rob74 6 hours ago | parent | next [-] | | Yes, it's possible. It's also possible for experienced pilots to fly an airliner completely manually for the full duration of a transatlantic flight without crashing. Still, over the last decades, autopilots and other assistance systems with ever more sophisticated features have been developed, and I would argue that without this technology crashes would be much more frequent than they are today. Same goes for memory management: you can do it manually, and it might work out most of the time, but if the programming language is helping you with it, the likelihood of memory safety issues decreases drastically. And the problem with avoiding these issues only by "meeting certain standards of rigour" is that you might only find out that you have failed to meet them years later, when you learn that your software has a vulnerability. | | |
| ▲ | hresvelgr 5 hours ago | parent [-] | | > Same goes for memory management: you can do it manually, and it might work out most of the time, but if the programming language is helping you with it, the likelihood of memory safety issues decreases drastically. And the problem with avoiding these issues only by "meeting certain standards of rigour" is that you might only find out that you have failed to meet them years later, when you learn that your software has a vulnerability. Zig does help you. Array slices, explicit nullability of pointers, defer errdefer, explicit allocators, built-in leak detection, bounds checks, overflow detection, the list goes on. If you need to play around on that side of the fence, Zig gives you a lot to make sure you don't mess it up. If we were talking about C I'd give you your flowers, but we're not. The most common issues and vulnerabilities that crop in C from manually managing memory are strongly mitigated by a quarter of that list. |
| |
| ▲ | chlorion 6 hours ago | parent | prev [-] | | Just people like the linux kernel, all major browsers, most of our popular webservers, etc and basically all non-trivial software projects written in C. The good news here is that we have more than just anecdotes to support this, we have empirical evidence. |
|
| |
| ▲ | quietbritishjim 7 hours ago | parent | prev | next [-] | | That's like saying that they should have used assembler but they just weren't capable of it. It's personal insult dressed up as a nonsensical technical argument. | | | |
| ▲ | vintermann 8 hours ago | parent | prev | next [-] | | It matters what software you're writing. When it's a JavaScript runtime, it's not as if you can get away with preallocating all the memory you'll need like some games used to do. | |
| ▲ | blurbleblurble 9 hours ago | parent | prev | next [-] | | So it's reducible to a simple matter of inferiority vs superiority? | |
| ▲ | msdz 9 hours ago | parent | prev [-] | | Is engineering capability the issue, or time pressure/constraint? |
| |
| ▲ | cyber_kinetist 14 hours ago | parent | prev | next [-] | | I think the main issue is that Bun relies heavily on existing C++ libraries like JavascriptCore, and these require RAII and ref-counting semantics from C++ that are closer to Rust than Zig. You could write a JS engine with Zig-like idioms (arena allocation, static initialization), but that would require re-writing the whole JS engine from the ground-up (though I would definitely be interested in it if someone actually tries to do it!) | | |
| ▲ | kllrnohj 12 hours ago | parent | next [-] | | > You could write a JS engine with Zig-like idioms (arena allocation, static initialization) Arena allocators & static initializers are not novel. You'll find them in high performance C++ projects as well, such as LLVM or JavaScriptCore. But arena allocators have the quite significant limitation that they only help when everything being allocated in them have approximately the same lifetime. So they don't help when you need to allocate memory to provide the native implementation of a JavaScript object, for example (eg, FFI). | |
| ▲ | vintermann 8 hours ago | parent | prev | next [-] | | > You could write a JS engine with Zig-like idioms (arena allocation, static initialization) Could you actually? That seems like a bad fit for a JS engine to me. Predictable memory requirements are great when you can have them, perhaps you can avoid complexity then, but for a JS engine? | |
| ▲ | ttflee 14 hours ago | parent | prev [-] | | Why not using Swift? | | |
| ▲ | norman784 11 hours ago | parent | next [-] | | Swift is very weak outside Apple ecosystem, compared to Rust. Not sure nowadays, but Swift used to have breaking changes each major release, that's a non go for a big project. | | |
| ▲ | ttflee 11 hours ago | parent [-] | | But Swift has a stable ABI which neither Zig nor Rust could provide. | | |
| ▲ | norman784 6 hours ago | parent | next [-] | | How important is stable ABI for projects like Bun? I think it only matters if you are building a shared library. | |
| ▲ | satyapr93 6 hours ago | parent | prev [-] | | only on macOS. On other platforms ABI is not stable. |
|
| |
| ▲ | Ygg2 8 hours ago | parent | prev [-] | | Andreas Kling talked about it. It boils down to C++ interop sucks (no surprise for lang made by Apple), ecosystem is tiny, Rust works well enough with LLMs. https://youtu.be/DbHjKi_jASY | | |
| ▲ | jabwd 5 hours ago | parent | next [-] | | He just wanted to use LLMs for coding, and not enough training data on Swift code exists for his use case. Admitting to that would be rather silly, so here we have this sentiment now exist rent free in people's brains. The C++ interop of Swift is perfectly fine, to such a degree that FoundationDB is now using it effectively alongside its C++ origins. | |
| ▲ | pjmlp 6 hours ago | parent | prev [-] | | Swift is one of the few toolchains that actually has some kind of builtin C++ interop, alongside Objective-C++, D, .NET (via C++/CLI), Carbon (eventually). |
|
|
| |
| ▲ | eddd-ddde 3 hours ago | parent | prev | next [-] | | Zig is always _less_ robust than rust. Even if you have a single allocation you can always forget to free it. | |
| ▲ | 8 hours ago | parent | prev | next [-] | | [deleted] | |
| ▲ | hugmynutus a day ago | parent | prev | next [-] | | This reads like cope because you're re-inventing RAII from first principles. I cannot take this seriously as tutorials on robust Zig Allocation Pools will store a deinit method for each item within the pool, so when the pool deinits, all internal objects can be deinit'd. That is just RAII & dtors from first principles, except with extra overhead of manually storing fat pointers yourself (and the bugs that come with this). Instead of using a language with builtin guarantees & optimizations around handling this so your object pools don't need to carry around a bunch of function pointers. C++ has aggressive de-virtualization passes so at runtime a lot of the 'complex object hierarchies' can be flattened to purely static function calls. | | |
| ▲ | MintPaw 15 hours ago | parent | next [-] | | This is a general problem with destructors, you can't "batch delete" objects. To free a lot of stuff you're required to go pointer by pointer through the tree to clean up each object. To get real performance gains from pools you can't have per-object/subobject custom cleanup code. | | |
| ▲ | aabhay 12 hours ago | parent [-] | | Not necessarily. Drop semantics are just syntactic sugar, and can thus be aggressively inlined or auto vectorized by the compiler. |
| |
| ▲ | jstimpfle a day ago | parent | prev [-] | | I've argued elsewhere some things that are wrong with RAII and C++ objects in general. Here I would just like to mention that if you have to rely on "de-virtualization" passes, you're in a miserable situation architecturally. If you have code where the overhead of virtual function calls might be too much to pay, don't do virtual functions then. End of story. To deconstruct a pool of objects, I don't see what should ever be wrong with a function pointer. The overhead of loading the function pointer will get divided by the number of objects being deconstructed. Care to explain what's the issue here? | | |
| ▲ | hugmynutus a day ago | parent [-] | | > I don't see what should ever be wrong with a function pointer. [...]Care to explain what's the issue here? 1. You're writing code you don't have to 2. That adds runtime overhead 3. That when you screw up has non-trivial security & resource management side effects This is objectively indefeasible in nearly any vaguely professional context. | | |
| ▲ | jstimpfle 20 hours ago | parent [-] | | 1. No, you're not writing code you don't have to. It's not different to implementing this as non-virtual methods, in fact I'd argue doing simple functions is more straightforward. 2. And the code being compiled is abstract & generic, it won't be instantiated for every type and bloat the executable or instruction cache. 3. Security concerns: With C++ virtual methods every object carries a mutable pointer too (to a vtable containing function pointers). What resource management side effects please? | | |
| ▲ | kllrnohj 15 hours ago | parent [-] | | Re #3: vtable pointers aren't mutable...? | | |
| ▲ | jstimpfle 11 hours ago | parent [-] | | Of course they are. The pointers to the vtable are part of the object. They aren't mutable fields as per the language, but for security concerns it doesn't matter what the language thinks. Being part of the object, the vtable pointer has to live in a writeable memory mapping (like stack / heap). | | |
| ▲ | Conscat 8 hours ago | parent | next [-] | | Clang pointer authentication makes any type of vtable attack impossible in C++. | | |
| ▲ | jstimpfle 7 hours ago | parent [-] | | Fair enough, this is an extension though and I suppose you could use it with manually constructed vtables as well? |
| |
| ▲ | kllrnohj 6 hours ago | parent | prev [-] | | Then I don't understand your argument. If you're just saying what could go wrong with heap corruption, then your vtable complaint also applies to storing function pointers in arena allocators in Zig? Zig doesn't have anything special here? | | |
| ▲ | jstimpfle 3 hours ago | parent [-] | | I asked you what's wrong with pool destructor function pointers. You gave a reason what's wrong and I refuted it. So no, I'm not saying that storing a function pointer is special, just that nothing's wrong with it. (And implying that since there's nothing wrong and it's probably the most straightforward thing to do, it's also probably the right thing). |
|
|
|
|
|
|
| |
| ▲ | mrothroc a day ago | parent | prev [-] | | [dead] |
|
|
| ▲ | boutell 8 hours ago | parent | prev | next [-] |
| I thought that this was just an initial translation to unsafe rust in which they haven't actually gained any of those benefits yet, although presumably they can go there quickly now. |
|
| ▲ | BearOso a day ago | parent | prev | next [-] |
| > 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 15 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 12 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 10 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 9 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. |
|
|
| ▲ | gchamonlive a day ago | parent | prev | next [-] |
| > Rust does this automatically. It removes an entire class of errors from his backlog. Even with the huge amount of "unsafe" rust currently in bun? https://news.ycombinator.com/item?id=48967630 |
| |
| ▲ | zamadatix 18 hours ago | parent | next [-] | | As opposed to every commit abd line of code being unsafe? They use a lot of C/C++ libraries with their own code so unsafe isn't even a a problem in most places they use it. | |
| ▲ | theshrike79 21 hours ago | parent | prev | next [-] | | You think it’ll all stay there? Of course they’ll iterate and remove the unsafe bits which were necessary for the transition | | | |
| ▲ | nestorD 15 hours ago | parent | prev [-] | | Fun fact, unsafe does not let you turn off the borrow checker in Rust: https://steveklabnik.com/writing/you-can-t-turn-off-the-borr... | | |
| ▲ | adwn 12 hours ago | parent [-] | | "Fun fact", it lets you largely circumvent the borrow checker by creating arbitrary lifetimes: https://news.ycombinator.com/item?id=48974824 | | |
| ▲ | josephg 8 hours ago | parent | next [-] | | People are so funny about rust. “Safe rust isn’t expressive enough!” -> then use unsafe rust. “Unsafe rust lets you do anything! Even crazy things!” -> then use safe rust. Or just don’t write crazy code? Does bun actually do anything insane like that in its unsafe blocks? Or are you just fear mongering? | | |
| ▲ | adwn 6 hours ago | parent | next [-] | | Please try to interpret comments in the context of the discussion, not floating freely in a vacuum. The context of the discussion is the Bun port's excessive use of unreviewed unsafe blocks. unsafe in Rust can easily be misused to create Undefined Behavior, which renders any safety guarantees otherwise ensured by Rust's borrow checker invalid. > Does bun actually do anything insane like that in its unsafe blocks? Who knows? At 10k unreviewed uses of unsafe, I'd guess there are quite a few incorrect ones. LLMs don't produce perfect code (neither do humans), so there's a high probability that at least some of those create UB. | |
| ▲ | throwaway613746 5 hours ago | parent | prev [-] | | > Does bun actually do anything insane like that in its unsafe blocks? How would anyone even know, it's vibe coded. |
| |
| ▲ | z0ltan 10 hours ago | parent | prev [-] | | [dead] |
|
|
|
|
| ▲ | kimjune01 4 hours ago | parent | prev | next [-] |
| i found the same thing with Rust, verification steps are really helpful for speed and correctnesss |
|
| ▲ | bluegatty 15 hours ago | parent | prev | next [-] |
| Good thing there are tons of languages that do that out of the box, and which are frankly quite fast. |
|
| ▲ | latortuga a day ago | parent | prev | next [-] |
| I seem to recall this Rust rewrite is all "unsafe" meaning it really doesn't automatically eliminate those issues. |
| |
| ▲ | mrothroc a day ago | parent | next [-] | | Relevant passage from Jarred's post: "At the time of writing, about 4% of Bun's Rust code sits inside an unsafe block (~13,000 unsafe keywords across ~27,000 lines / ~780,000 lines), and 78% of those blocks are a single line — a pointer that came from C++, or one call into a C library. I expect this number to go down over time as we refactor from a faithful Zig port (which had no greppable unsafe keyword) to idiomatic Rust, but we are going to continue using C & C++ libraries like JavaScriptCore so it will always have more unsafe than pure Rust projects." | | |
| ▲ | dwattttt 15 hours ago | parent | next [-] | | Relative "number of unsafe keywords" or "lines inside unsafe blocks" isn't a good metric. It's unsafe to call a C library that gives you a raw pointer, that can be a single line. It's unsafe to use that pointer, that could be a second single line. Carrying that pointer around, the data structures it's in, that's all safe, and doesn't implicate lifetime checking at all, so Rust will let you do silly things with the actual lifetime. A better metric would be absolute unsafe keywords (because each carries a need to review), or "code in a module that uses unsafe anywhere" vs total code, because module boundaries isolate unsafe. | | |
| ▲ | NohatCoder 9 hours ago | parent [-] | | Yeah, one needs to understand that "unsafe" does not mark which parts of the code are actually unsafe, it simply marks parts where the compiler ignores parts of its rule set. But the implications can crop up anywhere, there is no guarantee that a resulting use-after-free or similar can only happen inside the unsafe blocks. In short, if you use an unsafe block, then potentially any part of your code is unsafe. | | |
| ▲ | josephg 8 hours ago | parent | next [-] | | > In short, if you use an unsafe block, then potentially any part of your code is unsafe. The way I think about it is that the unsafe keyword is a promise that you’re going to maintain the safety invariants yourself, manually. The program is memory correct if it correctly maintains a certain set of invariants. Unsafe punts responsibility over those invariants to the programmer. If you use unsafe and mess up, the program may be in an invalid state. Yes - it might crash, anywhere. But the bug is still almost always in an unsafe block. It’s often possible to make fully safe wrappers around unsafe code which maintains all those invariants manually. (Either statically or dynamically.). A lot of the rust standard library does this. For example, Vec, Box and slice all use unsafe code internally to create safe APIs. | | |
| ▲ | DSMan195276 3 hours ago | parent [-] | | > But the bug is still almost always in an unsafe block. That's entirely dependent on how you write your Rust code. If you're derefing an invalid pointer then the bug is usually in how you calculated that pointer value, but the only part that actually requires 'unsafe' is the deref, not the bugged pointer calculation. Now in properly written Rust code you should be marking all of that code as 'unsafe' in that case and documenting what invariants need to be maintained, but that's entirely on you to do. The only part the compiler actually enforces is that you mark the specific spots where you make use of the operations that 'unsafe' allows. |
| |
| ▲ | dwattttt 6 hours ago | parent | prev | next [-] | | > it simply marks parts where the compiler ignores parts of its rule set This is a common misconception, in a literal interpretation. It doesn't invalidate your point, but since people get the wrong impression: unsafe doesn't turn off any of the checks the Rust compiler does. It allows you to perform 5 additional operations, and that's it. Using those operations wrong is what breaks the safety promises of the language. As an example, you could dereference a raw pointer and tell the Rust compiler it lives forever, when it's really a pointer to an object that's about to be freed. | |
| ▲ | randysalami 3 hours ago | parent | prev [-] | | Right, so if I understand correctly, to make the code fully safe, it can be impossible? Because you have underlying dependencies which are unsafe. The best you can do is make the handling of the unsafe as safe and as restricted as possible. But I can imagine some of these are way more difficult than others, and in programming the last 10% can be the last 90% of the work. Alas in terms of perceptions, the argument works well. |
|
| |
| ▲ | Yokohiii 12 hours ago | parent | prev [-] | | Why not porting JavaScriptCore? |
| |
| ▲ | nsonha 9 hours ago | parent | prev [-] | | what is surprising about it? The way porting works has alway been first doing a faithful pass before going back to address impedance mismatches case by case. You would do that even when porting things manually, let alone in a super risky completely automated bulk migration. |
|
|
| ▲ | raverbashing 8 hours ago | parent | prev | next [-] |
| > He talks about the issue of tracking memory lifecycles manually in zig so it can be explicitly freed. As expected, this leads to a long list of bugs where people missed things. Yes, and again the "you're holding it wrong" people or "you are not a good enough developer" people will try to do juggling with a chainsaw and lose a couple of fingers in the process Claude Code's endorsement (and real-world testing) speaks louder than internet discussions that are at this point 30 years old (and probably more) |
| |
|
| ▲ | portly 20 hours ago | parent | prev | next [-] |
| LLMs catch memory bugs quite easily in my experience. All of this is just a (succesful) marketing stunt by Anthropic. |
|
| ▲ | zombot 13 hours ago | parent | prev | next [-] |
| > "make it compile" is a pretty good target. But only as far as "make it compile" is a good predictor of runtime behavior. In C++ for instance, I can "make it compile" and it still crashes at runtime or does other undesirable things. |
|
| ▲ | m00dy 13 hours ago | parent | prev | next [-] |
| Rust is the clear winner of LLM era, you can't say otherwise. |
|
| ▲ | throwaway613746 5 hours ago | parent | prev [-] |
| [dead] |