← Back to context

Comment by barishnamazov

13 hours ago

This reminds me of the Servo project's journey. Always impressed to see another implementation of the WHATWG specs.

It's interesting to see Zig being chosen here over Rust for a browser engine component. Rust has kind of become the default answer for "safe browser components" (e.g., Servo, Firefox's oxidation), primarily because the borrow checker maps so well to the ownership model of a DOM tree in theory. But in practice, DOM nodes often need shared mutable state (parent pointers, child pointers, event listeners), which forces you into Rc<RefCell<T>> hell in Rust.

Zig's manual memory management might actually be more ergonomic for a DOM implementation specifically because you can model the graph relationships more directly without fighting the compiler, provided you have a robust strategy for the arena allocation. Excited to learn from Lightpanda's implementation when it's out.

Hi, I am Francis, founder of Lightpanda. We wrote a full article explaining why we choose Zig over Rust or C++, if you are interested: https://lightpanda.io/blog/posts/why-we-built-lightpanda-in-...

Our goal is to build a headless browser, rather than a general purpose browser like Servo or Chrome. It's already available if you would like to try it: https://lightpanda.io/docs/open-source/installation

  • I see you're using html5ever for HTML parsing, and like it's trait/callback based API (me too). It looks like style/layout is not in scope at the moment, but if you're ever looking at adding style/layout capabilities to lightpanda, then you may find it useful to know that Stylo [0] (CSS / style system) and Taffy [1] (box-level layout) are both avaiable with a similar style of API (also Parley [2] which has a slightly different API style but can be combined with Taffy to implement inline/text layout).

    [0]: https://github.com/servo/stylo

    [1]: https://github.com/DioxusLabs/taffy

    [2]: https://github.com/linebender/parley

    ---

    Also, if you're interested in contributing C bindings for html5ever upstream then let me know / maybe open a github issue.

  • Off topic note: I read the website and a few pages of the docs and it's unclear to me for what I can use LightPanda safely. Like say I wanted to swap my it as my engine on playwright, what are the tradeoffs? What things are implemented, what isnt?

  • Would be helpful to compare Lightpanda to Webkit, Playwright has a driver for example and its far faster and less resource hungry than Chrome.

    When I read your site copy it struck me as either naive to that, or a somewhat misleading comparison, my feedback would be just to address it directly alongside Chrome.

  • Would be great if it could be used as a wasm library... Just saying... Is it? I would actually need and use this.

  • Choosing something like Zig over C++ on simplicity grounds is going to be a false economy. C++ features exist for a reason. The complexity is in the domain. You can't make a project simpler by using a simplistic language: the complexity asserts itself somehow, somewhere, and if a language can't express the concept you want, you'll end up with circumlocution "patterns" instead.

    Build system complexity disappears when you set it up too. Meson and such can be as terse as your Curl example.

    I mean, it's your project, so whatever. Do what you want. But choosing Zig for the stated reasons is like choosing a car for the shape of the cupholders.

    • Your Swiss Army Knife with a myriad of 97 oddly-shaped tools may be able to do any job anyone could ask of it, but my Swiss Army Knife of 10 well-designed tools that are optimal for my set of tasks will get my job done with much less frustration.

    • > C++ features exist for a reason.

      But sometimes not good ones. Lot's of domains make tradeoffs about what features of C++ to actually make use of. It's an old language with a lot of cruft being used across a wide set of problems that don't necessarily share engineering trade offs.

    • C++ features exist for a reason but it may not be a reason that is applicable to their use case. For example, C++ has a lot of features/complexity that are there primarily to support low-level I/O intensive code even though almost no one writes I/O intensive code.

      I don't see why C++ would be materially better than Zig for this particular use case.

    • That’s not fully true though. There’s different types of complexity:

      - project requirements

      - requirements forced upon you due to how the business is structured

      - libraries available for a particular language ecosystem

      - paradigms / abstractions that a language is optimised for

      - team experiences

      Your argument is more akin to saying “all general purpose languages are equal” which I’m sure you’d agree is false. And likewise, complexity can and will manifest itself differently depending on language, problems being solved, and developer preferences for different styles of software development.

      So yes, C++ complexity exists for a reason (though I’d personally argue that “reason” was due to “design by committee”). But that doesn’t mean that reason is directly applicable to the problems the LightPanda team are concerned about solving.

    • C++ features for complexity management are not ergonomic though, with multiple conflicting ideas from different eras competing with each other. Sometimes demolition and rebuild from foundations is paradoxically simpler.

    • A lot of them only still exist for backwards compatabilities sake though. And a decent amount because adding something as a language extension rather than building the language around it has consequences.

  • Respectfully, for browser-based work, simplicity is absolutely not a good enough reason to use a memory-unsafe language. Your claim that Zig is in some way safer than Rust for something like this is flat out untrue.

    • What is your attack model here? Each request lives in its own arena allocator, so there is no way for any potentially malicious JavaScript to escape and read memory owned by any other request, even if there is a miscode. otherwise, VM safety is delegated to the V8 core.

I don't think that a language that was meant to compete with C++ and in 10+ years hasn't captured 10% of C++'s (already diminished) market share could be said to have become "kind of the default" for anything (and certainly not when that requires generalising from n≅1).

  • It has for Amazon, Adobe, Microsoft, Google and the Linux kernel.

    It remains to be seen which big name will make Zig unavoidable.

    • > It has for Amazon, Adobe, Microsoft, Google and the Linux kernel.

      I don't think so. I don't know about Adobe, but it's not a meaningful statement for the rest. Those companies default to writing safe code in languages other than Rust, and the Linux kernel defaults to unsafe code in C. BTW, languages favoured by those projects/companies do not reliably represent industry-wide preferences, let alone defaults. You could certainly say that of the two languages accepted so far in the Linux kernel, the only safe one is Rust, but there's hardly any "default" there.

      > It remains to be seen which big name will make Zig unavoidable.

      I have no idea whether or not Zig will ever be successful, but at this point it's pretty clear that Rust's success has been less than modest at best.

      4 replies →

    • While certain teams within Google are using rust by default, I'm not sure rust is anywhere close in scale for new lines of code committed per week to c++.

      1 reply →

And use-after-free, when that arena's memory goes away.

  • Yeah that's certainly possible but leaking a pointer like this seems like it would be really easy to spot?

    • It's harder than you'd expect. Depending on what kind of bucketing an arena does (by size or by type), a stale reference may end up pointing to another piece of memory of the correct type, which is still wrong, but more subtly than a crash.

  • But arenas have substantial benefits. They may be one of the few remaining reasons to use a low-level (or "systems programming") language in the first place. Most things are tradeoffs, and the question isn't what you're giving up, but whether you're getting the most for what you're paying.

    • Arenas are also available in languages with automatic memory management, e.g. D, C# and Swift, to use only modern languages as example.

      Thus I don't consider that a reason good enough for using Zig, while throwing away the safety from modern languages.

      7 replies →

Too late now, but is the requirement for shared mutable state inherent in the problem space? Or is it just because we still thought OOP was cool when we started on the DOM design?

I don't think it's really that bad in Rust. If you're happy with an arena in Zig you can do exactly the same thing in Rust. There are a ton of options listed here: https://donsz.nl/blog/arenas/

Some of them even prevent use after free (the "ABA mitigation" column).

  • I'm not super experienced with zig, but I always think that in the same way that rust forces you to think about ownership (by having the borrow checker - note: I think of this as a good thing personally) zig makes you think upfront about your allocation (by making everything that can allocate take an allocator argument.).

    It makes everything very explicit, and you can always _see_ where your allocations are happening in a way that you can't (as easily, or as obviously - imo) in rust.

    It seems like something I quite like. I'm looking forward to rust getting an effects system/allocator api to help a little more with that side of things.

    • The problem is deallocation... unless you tie the allocated object to an arena allocator with a lifetime somehow (Rust can model that).

      1 reply →

    • That's true and I liked the idea of it until I started writing some Zig where I needed to work with strings. Very painful. I'm sure you typically get a bit faster string manipulation code than what you'd get with Rust but I don't think it's worth the cost (Rust is pretty fast already).

      1 reply →

  • No, you can't do the same thing in Rust, because Rust crates and the standard library generally use the global allocator and not any arena you want to use in your code.

    • I mean you can store the nodes in an arena so you don't have to deal with the borrow checker getting upset with your non-tree ownership structure. That's the context. We weren't talking about arena use for speed/efficiency purposes. In that case you are right; it's much more awkward to use custom allocators in Rust.

> without fighting the compiler

It's unfortunate that "writing safe code" is constantly being phrased in this way.

The borrow checker is a deterministic safety net. Claiming Zig is easier ignores that its lack of safety checks is what makes it feel easier; if Zig had Rust’s guarantees, the complexity would be the same. Comparing them like this is apples vs. oranges.

  • That's a very narrow way of looking at things. ATS has a much stronger "deterministic safety net" than Rust, yet the reason to use Rust over ATS is that "fighting the compiler" is easier in Rust than in ATS. On the other hand, if any cost is worth whatever level of safety Rust offers for any project, than Rust wouldn't exist because there are far more popular languages with equal (or better) safety. So Rust's design itself is an admission that 1. more compile-time safety is always better, even if it complicates the language (or everyone who uses Rust should use ATS), and 2. any cost is worth paying for safety (or Rust wouldn't exist in the first place).

    Safety has some value that isn't infinite, and a cost that isn't zero. There are also different kinds of safety with different value and different costs. For example, spatial memory safety appears to have more value than temporal safety (https://cwe.mitre.org/top25/archive/2025/2025_cwe_top25.html) and Zig offers spatial safety. The question is always what you're paying and what you're getting in return. There doesn't appear to be a universal right answer. For some projects it may be worth it to pay for more safety, and for other it may be better to pay for something else.

    • You’re changing the argument. The point wasn’t whether more safety is “worth it”, but that comparing ease while ignoring which invariants are enforced is misleading. Zig can feel simpler because it encodes fewer guarantees. I’m not saying one approach is better, only that this comparison shifts the goalposts.

      1 reply →

    • > 1. more compile-time safety is always better, even if it complicates the language (or everyone who uses Rust should use ATS), and 2. any cost is worth paying for safety (or Rust wouldn't exist in the first place).

      You keep repeating this. It's not true. If what you said was true, Rust would have adopted HKT, and God knows whatever type astronomy Haskell & Scala cooked up.

      There is a balancing act, and Rust decided to plant a flag in memory safety without GC. The fact that Zig, didn't expand on this, but went backwards is more of an indictment of programmers unwilling to adapt and perfect what came before, but to reinvent it in their own worse way.

      > There are also different kinds of safety with different value and different costs. For example, spatial memory safety appears to have more value than temporal safety (https://cwe.mitre.org/top25/archive/2025/2025_cwe_top25.html)

      How did you derive this from the top 25 of CWEs? Let's say you completely remove the spatial memory issues. You still get temporal memory issues at #6.

      7 replies →

  • The fact that Zig doesn't have Rust's guarantees doesn't mean Zig does not have safety checks. The safety checks that Zig does have are different, and are different in a way that's uniquely useful for this particular project.

    Zig's check absolutely don't go to the extent that Rust's do, which is kind of the point here. If you do need to go beyond safe code in Rust, Zig is safer than unsafe code in Rust.

    Saying Zig lacks safety checks is unfortunate, although I wouldn't presume you meant it literally and just wanted to highlight the difference.

    • Thing is, those safety checks are also available in C and C++, provided that one uses the right tools like PVS and PurifyPlus (just to quote two examples), and now ongoing AI based tooling efforts for verification, thus the question is why a language like Zig in the 21st century, other than "I don't like either C++ or Rust".

    • I never said Zig has no safety features. What I said is true, though. If it would have Rusts guarantees (as in: The same) it would be more complex.