Fascinating.
They've shown the idea it is better on C++ interop is wrong.
I don't know enough to say Rust has same OO support as Swift, but I'm pretty sure it does. (my guess as a former Swift dev: "protocol oriented programming" was a buzzy thing that would have sounded novel, but amounted to "use traits" in rust parlance)
EDIT: Happy to hear a reply re: why downvotes, -3 is a little wild, given current replies don't raise any issues.
So the approach of having a new language that requires a full rewrite (even with an LLM) is still a bad approach.
Fil-C likely can do the job without a massive rewrite and achieving safety for C and C++.
Job done.
EDIT: The authors of Ladybird have already dismissed using Rust, and with Servo progressing at a slow pace it clearly shows that Ladybird authors do not want something like that to happen to the project.
So long as you don't mind a 2-4x performance & memory usage cost.
Igalia had five engineers working full time who turned that science project into v0.0.1 in less than two years.
I've been programming in Rust for 5 years and I could barely understand the code in their repo. Not because it was somehow advanced but because it didn't make any sense. It felt like that with every decision they could make, they always chose the hardest way.
On the other hand, I have never done any C++ (besides few tutorials) in my life and yet I found both Serenity/Ladybird and also WebKit to be very readable and understandable.
BTW: If anyone wants to reply that Rust is different then yes, of course it is - but that's the point, if there is a language that maps nicely to your problem domain, it's also very fast, and well-understood then why the hell you'd use a language that is well-known to NOT map to OOP?
> Job done.
Seems like you forgot a few stops in your train of thought, Speed Racer.
Except the only thing that makes OOP OOP: Message passing.
Granted, Swift only just barely supports it, and only for the sake of interop with Objective-C. Still, Swift has better OO support because of it. Rust doesn't even try.
Not that OOP is much of a goal. There is likely good reason why Smalltalk, Objective-C, and Ruby are really the only OOP languages in existence (some esoteric language nobody has ever heard of notwithstanding).
Functional programming is not "encapsulating data in 'objects'". Such a model would naturally feature methods like "void Die.roll()", "void list.Add(element)" which are definitely not functional programming (which emphasizes immutability, pure functions, composition, etc.)
They are both OOP like a football can be something that you use in a sport and something that flies you to the moon. Except it is not clear what flies you to the moon goes by "football".
> Such a model would naturally feature methods like "void Die.roll()", "void list.Add(element)" which are definitely not functional programming
Exactly, functional programming. `Die` and `list` encapsulate data and use higher order functions to operate on it, which is what defines functional programming.
> which emphasizes [...] pure functions
Functions without encapsulation is imperative programming. If you introduce encapsulation then, yes, you have functional programming, just like your examples above.
Immutability is a disjoined property that can optionally apply to all of these programming paradigms. OOP, functional, and imperative programs can all be immutable, or not.
Composition and encapsulation go hand in hand, so that one is also functional programming, yes. And certainly composition is core to languages like C++/Java/Rust/etc. Naturally, them being functional languages.
To reiterate:
- Imperative programming: Data and functions are separate.
- Functional programming: Data is grouped with functions.
- Object-oriented programming: Data is grouped with message responders.
These definitions are not at odds with the discussion at hand at all. It was clearly stated that Swift has better OO support. Which is obviously true because it tries to be compatible with Objective-C, and therefore needs to have some level of OO support. That is something that Rust has no interest in doing, and rightfully so.
Your redefinition violates the claim, and therefore we can logically determine that it is not what was being used in the rest of the thread. That is, aside from the confused Rust guy that set this tangent in motion, but the remainder of this thread was merely clarifying to him what was originally intended. "He has a different definition for OOP" doesn't really help his understanding. That is what this thread branch has always been about, so it is not clear where you are trying to go with that.
(source: iOS dev from jailbreak days, so like 8 years before Swift, till 2019. He did not mean dynamic dispatch and Swift has dynamic dispatch by way of "you can annotate a Swift method with @objc and we'll emit it as an ObjC method instead of Swift", not Smalltalk-ish, like, at all. if you're the poster who originally said "because of dynamic dispatch", I understand why you're frustrated but I have 0 idea why you think dynamic dispatch in Swift would matter, much less say it makes Swift much better at OOP than Rust. It's impolite to say "utterly baffling engineering decision" in public, so there's subtext in the conversation. Namely that both claims made 0 sense if you had practical familiarity with either)
But that's the thing, there is: It supports message passing.
Like we already discussed long ago, it supports it poorly, and only for the sake of compatibility with Objective-C, but still that makes its OOP support better than Rust's. Rust has no OOP support at all. It is not an OOP language and never would want to be. OOP goes completely against its core principles (statically-typed, performance-minded).
Realistically, nobody would consider Swift an OOP language either. However, on the spectrum, it is unquestionably closer to being an OOP language. It at least gets an honourable mention in the short list of OOP languages. It is undeniable that Swift has "better" OOP support; not to be confused with good OOP support.
> He did not mean dynamic dispatch
Of course not. Dynamic dispatch is for function calling. OOP doesn't use dynamic dispatch. That's literally why we call it object-oriented rather than function-oriented (or functional, as the kids say). This is also why Objective-C, quite brilliantly, uses [foo bar] syntax (hint: it kind of looks like an ASCII-art envelope for a reason): To make it clear that conceptually you are not calling a function.
> I understand why you're frustrated
I don't. Fill us in.
You're using a definition of OOP where only Smalltalk-style message passing counts. By that definition, you're right: Swift is closer, because `@objc` exists. But by that definition, neither Swift nor Rust is an OOP language in any meaningful sense, and the delta between them is mass-of-the-electron tiny. Swift's message passing support is an annotation that is a compatibility shim for a 40-year-old runtime, not a design philosophy.
So when you say Swift has "better OO support"... sure, in the same way that my car has better submarine support than yours because I left the windows cracked and water could theoretically get in. Technically true! Not useful!
The Ladybird team are C++ developers evaluating languages for a browser engine. When C++ developers say "OOP" they mean classes, inheritance hierarchies, virtual methods. The DOM is a giant inheritance tree. That's the context. You can tell them they're using the word wrong, but that doesn't change what they meant, and what they meant is the only thing that matters for understanding whether the claim made sense.
And under their definition-which is also the definition used by mass, essentially every working programmer and every university curriculum for the last 30 years-Swift and Rust are actually pretty close, which was my original point. Swift has `class` with inheritance, Rust has traits and composition. Neither is Smalltalk. The claim that Swift is "strictly better" was weird no matter which definition you pick.
(Also, and I say this respectfully: characterizing C++/Java/Rust as "functional programming" because they encapsulate data with functions is... a take. I get the logic chain you're following but that is not a definition that will help anyone communicate with anyone else, which is presumably the point of definitions.)
Okay, but for that to be true in this case then you must explain how Swift has "better OOP support". If there is no rational explanation for now Swift has "better OOP support" by the metrics you are imagining, as you, me, and everyone else has alluded to earlier, then clearly that isn't what they meant.
> I get the logic chain you're following but that is not a definition that will help anyone communicate with anyone else
Won't help communicate with anyone else meaning that you are the only one capable of understanding the logic chain? I'm sure you are a talented guy, but the world isn't exactly lacking in talented people. I'm sorry to say, but most everyone on HN will have absolutely no trouble understanding this.
I did. Swift has `class` types with implementation inheritance. Rust does not. If you're porting a C++ codebase with deep class hierarchies (like, say, a browser DOM), Swift lets you transliterate those hierarchies directly. Rust makes you rethink them into composition and traits. That's a real difference that matters to a team mid-migration, and it's an extremely rational explanation for why a C++ dev would say Swift has "strictly better OOP support."
You don't have to agree it's a large difference (I don't, which was my original point), but "there is no rational explanation" just isn't true. There's a very obvious one, it's just boring.
> If there is no rational explanation, as you, me, and everyone else has alluded to earlier, then clearly that isn't what they meant.
My position was never "there is no rational explanation." My position was "the difference is small enough that 'strictly better' was a weird thing to say." Those are different claims! You're kind of merging me into your argument when we don't actually agree.
> Won't help communicate with anyone else meaning that you are the only one capable of understanding the logic chain?
No, I meant that if you say "C++ is a functional programming language" to mass, any working programmer, they will not understand you, because that is not what those words mean to them. It's not about intelligence, it's about shared vocabulary. You've built a internally-consistent taxonomy where functional = data + functions, OOP = data + message passing, and imperative = data and functions separate. I can follow it fine. But you've redefined three terms that already have widespread, different meanings, and then you're treating disagreement as confusion. That's the communication problem.
I'm certainly not clever enough to have built it. Not to mention that the person who coined OOP is quite famous for having done so. I am not him, I can assure you. I have merely ingested it from what is out there in widespread circulation.
I can appreciate that you live in a different bubble and what is widespread there is not the same. It's a pretty big world out there. However, it doesn't really matter as if "C++ is a functional programming language" doesn't jive with your understanding, as you'll simply ask: "What ever do you mean?" and which point "functional programming language" will be defined and a shared understanding will be reached.
This isn't the problem you are imagining.
> I did.
Right. Seems we encountered a communication barrier again. "That's the thing man, there isn't anything." in my world would read "That's the thing man, there are things and here they are: ..." However, this highlights again that it doesn't actually harm communication as further clarification follows and eventually everyone will reach a shared understanding. Communication isn't some kind of TV game show where you have to get the right answer on your first try. This is not a problem in any way, shape, or form.
Ha, don't sell yourself short, you're doing a great job defending it.
> However, it doesn't really matter as if "C++ is a functional programming language" doesn't jive with your understanding, you'll simply ask: "What ever do you mean?"
Okay, genuinely, let's try this exercise. You say to me "C++ is a functional programming language." I ask "What ever do you mean?" You say "data is grouped with functions." I say "...that's also true of Python, JavaScript, Kotlin, Scala, Dart, TypeScript, and basically every language designed after 1990. What term do you use for Haskell?" And now we're in another 20-message thread defining terms from scratch instead of talking about the actual thing.
Like, you've got a taxonomy where imperative/functional/OOP is a clean trichotomy based on how data relates to code. That's elegant! But it also means "functional programming" contains both Haskell and Java, which in practice need to be distinguished from each other far more often than they need to be grouped together. The Kay-pure definitions give you clean categories at the cost of useful ones.
*Obj-C doesn't even pass muster of the Kay-pure definition, which renders the whole conversation moot.*
> "That's the thing man, there isn't anything." in my world would read "That's the thing man, there are things and here they are: ..."
Okay, fair hit. :) What I meant was: there's nothing that would make a C++ team say "strictly better." Swift has classes with inheritance, sure. But "strictly better" implies Rust can't even get close, and it can-you just model things differently. The Ladybird team discovered this themselves, which is... kind of the whole story here? They said "strictly better OOP support," tried it, and now have removed Swift. The claim didn't survive contact with their own codebase. That was the entire point of my original comment sitting at -3. (now at +2)
> Communication isn't some kind of TV game show where you have to get the right answer on your first try.
No, but Hacker News comments at -3 do get grayed out and collapsed, so in practice it kind of is, unfortunately.
In the context of the dimension we have been talking about, it is also functional. There is no difference between Haskell, Python, Java, etc. in that particular dimension. All of those languages you list are quite different in other dimensions, of course. Are you under the impression that programming languages are one dimensional? Unfortunately, that is not the case.
> And now we're in another 20-message thread defining terms from scratch instead of talking about the actual thing.
Especially when we find out that what we really wanted to talk about was type systems. Thinking of programming languages as being one dimensional is a fool's errand.
> But it also means "functional programming" contains both Haskell and Java, which in practice need to be distinguished from each other far more often than they need to be grouped together.
Right, there may be a need to separate them, but sensibly you would separate them on the dimension that is relevant to the separation intent, not some other arbitrary quality. For example, perhaps your interest is in separating mutability and immutability. Therefore, something like "Haskell is an immutable-by-default programming language" would be an appropriate statement in that desired context. "Haskell is a statically-typed programming language", not so much.
> No, but Hacker News comments at -3 do get grayed out and collapsed, so in practice it kind of is, unfortunately.
I'll still read your comments if they turn grey. I don't care about what color they are. This isn't a problem.
Why not D?
Hoo boy, wait until you hear about the state of C++'s library "system"
> Swift is strictly better in OO support and C++ interop
So I guess from their point of view that’s why not rust.
I don’t have a horse in the race.
I was genuinely interested in why they didn’t even consider D given they already ruled out rust for those particular reasons, for which it seems D would fulfill nicely.
Probably the same reason why Rust is problematic in game development. The borrow checker and idiomatic Rust do not go well together with things that demand cyclic dependencies/references. Obviously there are ways around it but they're not very ergonomic/productive.
Said differently: the C++ interop did not support calling the C++ library I wanted to use, so I wrote a C wrapper.
He even made an attempt at creating his own language, Jakt, under SerenityOS, but perhaps felt that C++ (earlier with, now without Swift) were the pragmatic choice for Ladybird.
I think you are conflating the development of Servo with the design and development of Rust.
Tools to do X better are often designed by people who get paid a lot to do X and worry about losing their job if they are not good enough at X.
If he were to tell me that he didn't imagine Rust's helping with browser dev when he designed Rust, then I'd believe him, but the "circumstantial" evidence points strongly in the other direction.
Rust was born at Mozilla, sort of. It was created by a Mozilla employee. The first "real" project to put it into action was Servo of which parts were adopted into Firefox. While Rust may not have been developed "specifically" to create a browser, it is a fair comment.
That said, Ladybird was started as part of the SerenityOS project. That entire project was built using C++. If the original goal of Serenity was to build an opeerating system, C++ would have felt like a reasonable choice at the time.
By the time Ladybird was looking for "better" languages than C++, Ladybird was already a large project and was making very heavy use of traditional OOP. Rust was evaluated but rejected because it did not support OOP well. Or, at least, it did not support integration into a large, C++ based, OOP project.
Perhaps, if Ladybird had first selected a languge to write a browser from scratch, they would have gone with Rust. We will never know,
We do know that Mozilla, despite being the de facto stewards of Rust at the time, and having a prototype web browser written in Rust (Servo), decided to drop both Rust and Servo. So, perhaps using Rust for browsers is not as open and shut as you imply.
20240810 https://news.ycombinator.com/item?id=41208836 Ladybird browser to start using Swift language this fall
- Excellent for short-lived programs that transform input A to output B
- Clunky for long-lived programs that maintain large complex object graphs
- Really impressive ecosystem
- Toxic community
Lots of people seem really committed to OOP. Rust is definitely a bad fit if you can't imagine writing code without classes and objects. I don't think this makes rust is a bad language for the problem. Its just, perhaps, makes rust a bad language for some programmers.
Traits give you the ability to model typical GUI OO hierarchies, e.g.:
trait Widget {
fn layout(&mut self, constraints: Constraints);
fn paint(&self, ctx: &mut PaintCtx);
fn handle_event(&mut self, event: Event);
}
struct Button { ... }
struct Label { ... }
impl Widget for Button { ... }
impl Widget for Label { ... }
let mut widgets: Vec<Box<dyn Widget>> = Vec::new();
Implementation inheritance can be achieved with good old shared functions that take trait arguments, like this: fn paint_if_visible<W>(widget: &W, ctx: &mut PaintCtx)
where
W: HasBounds + HasVisibility,
{
if widget.is_visible() {
ctx.paint_rect(widget.bounds());
}
}
You can also define default methods at the trait level.This all ends up being much more precise, clear, and strongly typed than the typical OO inheritance model, while still following a similar overall structure.
You can see real world examples of this kind of thing in the various GUI toolkits for Rust, like Iced, gpui, egui, Dioxus, etc.
> Especially because there's no GC.
This is the only real issue I can think of. However, for implementing something like a UI, automatic GC isn't really necessary because the lifetime of widgets etc. maps very well to the lexical/RAII model. Windows own widgets, etc. Again, see all the UI toolkits implemented in Rust.
Not so sure about not needing GC. Many times a problem seems easy without GC until you get into the weeds. Like why did ObjC feel the need to add ARC (not GC but similar goal)?
It sounds like you're speculating. But this doesn't match the reality of actual Rust code. For example, here's the complete, actual code for the `Widget` trait implementation for the `Button` struct in the egui framework:
impl Widget for Button<'_> {
fn ui(self, ui: &mut Ui) -> Response {
self.atom_ui(ui).response
}
}
(From: https://github.com/emilk/egui/blob/main/crates/egui/src/widg... , bottom of page.)This is OO code. It's just that it's trait-oriented, not class-oriented. But that's a good thing, not a drawback. Traits are more precise, more flexible, more type-safe, and don't conflate multiple unrelated concerns in a single feature the way class inheritance does. OO languages like Java support a very similar approach, via interfaces.
You can find similar code in any Rust GUI library, and indeed in almost any non-trivial Rust program.
> Otherwise, there'd be no difference and everyone would call Rust an OO language.
Rust is not a class-based OO language. But the fact that one of its core features, traits, have "methods", and those methods have a first argument traditionally named `self` should be a clue. Trait-based OO is very much OO. Whether "Rust is an OO language" depends on how strictly you want to define "OO" to match the rather obsolete 1970s conception of it.
This is why the first question I asked you was "What OO features are you thinking of that Rust doesn't have?" Because "OO" is a broad label that covers a wide range of different features.
Rust doesn't have "classes", but classes are a 1970s-era mish-mash of a whole bunch of different concerns munged together into a single poorly-factored construct. Now, half a century later, that's well understood, and better solutions exist in the form of traits and interfaces, and related capabilities.
> Like why did ObjC feel the need to add ARC (not GC but similar goal)?
Same reason Rust includes `Rc` and `Arc`. A general-purpose language has to provide some solution to memory management based on reachability. Reference counting and tracing GC are common solutions to that. Rust's static memory management plus RC is plenty good enough for GUI applications.
GTK, Qt, DOM, WinUI 3, Swing, Jetpack Compose, and GWT (to name a few) all provide getters and setters or public properties for GUI state, violating the encapsulation principle [1]. The TextBox/EditBox/Entry control is the perfect example.
The impedance mismatch is that a GUI control is not an object [2]. And yet, all of the object-orient GUI examples listed implement their controls as objects. The objects are not being used for the strengths of OO, it's just an implementation detail for a procedural API. The reason these GUIs don't provide an API like shown in [1] is because it's an impractical design.
"How are you supposed to design an OO TextEdit GUI control if it can't provide a getter/setter for the text that it owns?" Exactly. You're not supposed to. OOP is not the right model for GUIs.
Ironically, SwiftUI doesn't have this problem because it uses the Elm Architecture [3] like React and iced.
[1]: https://www.infoworld.com/article/2163972/building-user-inte...
[2]: From [1], "All the rules in the rule-of-thumb list above essentially say the same thing — that the inner state of an object must be hidden. In fact, the last rule in the list (“All objects must provide their own UI”) really just follows from the others. If access to the inner state of an object is impossible, then the UI, which by necessity must access the state information, must be created by the object whose state is being displayed."
It isn't a _tracing_ GC