119 comments

[ 5.7 ms ] story [ 145 ms ] thread
Having fun with this.

Never bought into rust (have studied, have a (mostly AI-generated app in rust).

Wrote some Zig but Odin is even less overhead for me. I first loved Zigs built-in build system but having tried to wrap/use C libraries from both, I must say I prefer Odin. Wrapping some sqlite 3 API’s for my first little Odin program - just because I need so little of the API that it seems easier this way - and speaking to C from Odin is a pleasure.

That is, imho, where Rust fails the most - the second part is the C++’ish approach to memory management (RAII) - that’s not how systems programming or games (I’m told) tend to work.

To each their own. I had some fun with Rust too, but for me, Odin seems the most appealing :)

That is, imho, where Rust fails the most - the second part is the C++’ish approach to memory management (RAII) - that’s not how systems programming or games (I’m told) tend to work.

What does this mean? Who told you that?

Casey Muratori and Jon Blow have pushed this concept frequently.

They largely don't deeply elaborate, which is sad because I am a professional game developer who is interested in precisely presented knowledge so I can apply it to my work.

My interpretation is that games want to allocate large pools of resources, like GPU buffers, cpu memory, etc, and reuse that memory over and over. Ie: Reinitialize it.

In my experience, RAII is my preferred pattern for certain things (std::lock_guard), and you can almost certainly express the majority of these "uber game dev patterns" using RAII/smart pointers/etc, but the c++ implementation of these "uber game dev patterns" tends to be more complicated (and imo esthetically ugly) compared to really well written C.

These new languages (zig, odin, jai) appear to be attempts to improve C to allow an alternative to C++ that doesn't have the ugly baggage that C++ has.

Casey Muratori and Jon Blow are both hyper-dogmatic "my way or the highway" types, and, crucially, neither of them has built any of the super high fidelity types of game that would require that level of optimisation. They're basically influencer types.
Casey worked on tooling for AAA games that most certainly needed “that level” of optimization.

Jon Blow worked on numerous AAA games that required “that level” of optimization. And he’s one of the very few developers in the last 15 years who have managed to sell more than a million copies of a game running on a scratch built 3D engine.

You can disagree with their opinions, but they certainly have the experience to back those opinions up.

Well, Minecraft sold far more and it's a scratch built 3d engine isn't it? Popularity is certainly not a technical achievement.
Minecraft doesn't use an off the shelf engine, but I wouldn't really call Minecraft scratch built. It's built on top of LWJGL.

Popularity isn't a technical achievement in itself. But what is a technical achievement is that he built a visually impressive 3d engine that worked well enough for over a million people to buy it at a time when almost no one else was doing this. Minecraft was released nearly 10 years earlier--before Unity and Unreal had completely taken over.

I'm also not saying that the witness engine is somehow better than anything else out there, but it's a significant enough technical accomplishment that along with Jon Blow's other work, he has the experience to back up his opinions.

I've also never seen the critique that Jon Blow is just an influencer with no relevant experience from anyone with equivalent relevant experience. I've seen other experienced game devs who disagree with him, but I've never seen them say that he doesn't have the experience to have them.

Only because there are no native OpenGL bindings for Java from Khronos, hence LWJGL.
I don't have any experience with Java game development, but I was under the impression that LWJGL provided more than just OpenGL bindings.
It is indeed a bit like using SDL, however pure OpenGL bindings like JOGL have seen better days.

Also, if you are doing a game in Java, you also need bindings for sound APIs, and other ones that traditionally only care about C, thus you end up with something like LWJGL anyway.

Well, it's more than just OpenGL bindings mostly because it's bindings for a bazillion other things too. Just look at the javadoc[0]. The non-binding stuff in there is just utilities to make the bindings at least somewhat idiomatic in Java. In practice, LWJGL games are almost always just GLFW games, which I would still qualify as being pretty damn close to "from scratch".

0. https://javadoc.lwjgl.org

Dunno, the Witness had gorgeous lighting. Both have consulted for AAA too.
They really are not - there used to be whole schools of thought on how to lay out applications in memory, using the freedom of C before C++ came along, and standardized on a bunch of solutions that are controversial in some circles - stuff like vtables and exceptions are the reason C++ is banned in the kernel.

And with things like Java, you have even less visibility. They made a ton of interviews with oldschool devs, and a most of them did things differently from how Jon and Casey do, but it's not like they disapprove, it's just with modern languages, you don't get the level of control to do this.

From what I understood, their critique of RAII is twofold: coupling of allocation and initialisation, and enforcement of deallocation. The ease of use of smart pointers makes it tempting to allocate/free of temporary structures even within one single function. Given enough number of such occurrences, it kills performance by a thousand cuts. Also I remember they mentioned it’s not necessary to free memory if you’re about to close your program, because the OS will take the memory back. Obviously you need to gracefully deinitialise some things, like audio or other devices, but that’s beyond the discussion.

As on some references, Ryan Fleury did an episode on Wookash podcast on RAD debugger showing ECS like approach.

IMO, their RAII critique is a but nuanced, but because of their personality the discourse often gets polarising.

Edit: the sibling comment just proved my last point.

most of that criticism only works on C++. rust does enforce RAII but uses stack allocation for locals by default instead of touching the heap so it skips the slow part.

on the other hand there are no constructors (just normal functions) so you cant initialize values in place, only stack allocate and return. i think rust needs to add in place init and change the rules from "always init at declaration site" to "must be initialized at first use" like kotlin.

the big missing piece is custom allocators that let you use something like a bump arena with the same convenience as system malloc. they already exist on nightly but nobody knows when they will land on stable.

honestly thats the biggest problem with rust, they come up with a lot of useful changes but then take ages to stabilize because the core team is overworked. they also have a kind of perfectionist culture as a reaction to all the half baked features shipping in C++.

there should be a stage between nightly and full release where feature is in stable toolchains behind a cfg flag and you get a warning if upstream crates use it. show commitment to shipping it in time but still make it clear that it can change (in minor incompatible ways) before release.

Thanks for this, I’m actually learning Rust (albeit slowly) atm, and still can’t wrap my head around how arena allocs would fit the borrow checker
I haven't written any serious Rust in a while, but I assume there is some kind of lifetime annotation involved. The "objects" allocated in the arena have to be explicitly given the same lifetime as the arena itself. I have no idea how that looks syntactically.
There are several libraries for doing arena allocation in Rust, e.g. bumpalo: https://crates.io/crates/bumpalo , see the documentation for examples (it's quite easy to use and fits the borrow checker very well). The comments in here regarding Rust are somewhat misleading; Rust doesn't require unstable/nightly features to do this sort of stuff. What's unstable in Rust right now is a standardized interface for generically working with allocators (see https://github.com/rust-lang/rust/pull/156882 for the most recent progress), including support for the containers provided by the stdlib.
> honestly thats the biggest problem with rust, they come up with a lot of useful changes but then take ages to stabilize because the core team is overworked

On the contrary, that has been one of Rust's biggest strengths. My impression when reading through stdlib was that they got so many things right, and for that to happen, things need to be thought out properly.

Case in point, it'd be such a shame if they stabilized the allocator API, only for us to forever regret never getting the storage API [1] instead, or vice-versa, depending on which one turns out to be more pragmatic.

> they also have a kind of perfectionist culture as a reaction to all the half baked features shipping in C++.

And that's a good thing! Some people really dislike the constant influx of new features due to the overwhelming complexity it leads to. So if we do have new features, they better be worth it.

[1] https://github.com/rust-lang/rfcs/pull/3446

The ease of use of smart pointers makes it tempting to allocate/free of temporary structures even within one single function. Given enough number of such occurrences, it kills performance by a thousand cuts.

This isn't true and doesn't make any sense. Smart pointers don't need to enter into it. If you need a lot of something you make a vector and allocate once.

Also I remember they mentioned it’s not necessary to free memory if you’re about to close your program, because the OS will take the memory back.

It is an extremely niche scenario to need a program to shut down so much faster that you can't even deallocate memory. If you don't make lots of small allocations in the first place the deallocations won't take any time.

Obviously you need to gracefully deinitialise some things, like audio or other devices, but that’s beyond the discussion.

It's actually a pretty big advantage to destructors to deal with stuff like this as well as memory and locks.

IMO, their RAII critique is a but nuanced, but because of their personality the discourse often gets polarising.

I think it gets polarizing because they are both undeniably sharp programmers but don't have any real evidence of this stuff, they are just grasping at rationalizations.

> Casey Muratori and Jon Blow have pushed this concept frequently

Ah... The school of what I like to call "maximum opinions and minimal evidence". Aggressive arrogant dismissal of anything except their exact view (and for Muratori, you're also "woke" for good measure), coupled with a complete lack of _hard evidence_ to back up their views. In that regard, they're not unlike "investment advice" instagram influencers.

I can see why you would think that about Jon Blow, though I disagree. But Casey? He has a number of thorough videos with plenty of evidence, and has never struck me as arrogant.
"coupled with a complete lack of _hard evidence_ to back up their views"

Didn't Casey Muratori write a proof of concept windows terminal to highlight Microsoft's substandard implementation? And Jonathan Blow has spent the past decade+ developing his own programming language and funding the development of a game. Seems like they are backing their views.

Apparently you're the school of dismissive person who can't even be bothered to look up their technical achievements.
Confusing achievements in one area for evidence in another is literally an appeal to authority fallacy.
You must mean confusing achievements in the area of creating large maintainable and performant systems on the one hand, with achievements in the area of creating large maintainable and performant systems on the other hand?

And let me incude Ryan Fleury here who graduated from Casey-school with honors, he is maybe the most impressive programmer I know. His work on the raddebugger is outstanding.

You're still not anywhere close to the thread's topic. Hero worship isn't evidence.

C++’ish approach to memory management (RAII) - that’s not how systems programming or games (I’m told) tend to work

This is the statement that no one seems to be able to back up. Instead it's fans of some internet programmers saying that they must be right about everything because they shipped a game.

I actually like hearing what they have to say, but they aren't right about this and there is no evidence or explanation here from you, them or anyone else.

Instead you start talking about a third person making a debugger for some reason as if the solution to appealing to authority is more appealing to authority.

> This is the statement that no one seems to be able to back up.

Actually there are many explanations of systemic issues arising from reliance on RAII. Both Jon and Casey are very good explaining the matters.

> Instead you start talking about a third person making a debugger for some reason as if the solution to appealing to authority is more appealing to authority.

A third person (already mentioned in a parent comment), who is creating an extremely snappy and powerful debugger using those exact approaches under discussion.

Actually there are many explanations

So lets hear them.

A third person (already mentioned in a parent comment), who is creating an extremely snappy and powerful debugger

Who cares? They have nothing to do with anything, why are you avoiding a technical discussion of facts to talk about your favorite influencers?

> So lets hear them.

There are few people delivering more in-depth explanations of their claims, like literally thousands of hours of content. You can just look it up. If you disagree, please disagree. Personally I find what they say articulates almost 100% of my independent experience. And I do systems programming in C++ full-time (file systems / desktop app infrastructure). Personally I've made at least 9 serious attempts of building robust and maintainable systems on top of C++ features like RAII and templates and have always returned to simple straightforward C-style code. And I've had to work many times with existing C++ codebases that had bought into these features, and it always seemed like a giant waste of time, a way to add more spaghettis to a hot mess. And also, any technically convincing C++ codebase I've seen always seemed to make only very light use of C++ features. Not going to argue more here.

> Who cares? They have nothing to do with anything

Quite the opposite, it's a third person from the same circle making the same kind of claims (there are more persons), who have, contrary to what you say, actual evidence to back up their claims. You could just go ahead and check these works out yourself and see if it convinces you that the methods they claim to be better are actually better.

These are classic zero evidence replies. Go and "do your own research", "look it up yourself", and then nothing but repeating your claims and not even realizing that isn't evidence.

Here it's actually worse, I don't even know what the specific claims you're talking about are.

If this was so simple and you know it so well just spell it out.

You can just look it up.

Or you can just look up what literally everyone else does and relies on in systems programming.

I've seen always seemed to make only very light use of C++ features.

So what? You not using something has nothing to do with an actual explanation of its shortcomings, you realize that right?

there are more persons

Then where is the actual evidence and explanation? Why is it so difficult even after multiple replies to have a shred of evidence or technical explanation?

It's you and the three guys you are fans of against the entire world of systems programming, because scope based resource management and ownership is what people need the vast majority of the time because most resource lifetimes are determined by scope.

If you're going to reply again, try to have some explanation of what your claim is and why it makes sense on a technical level instead of just trying to tell someone to go and prove your ambiguous claim for you.

Also saying that you ignoring something is somehow an explanation for why it doesn't work, is like someone claiming helicopters don't work because they don't use them to get around.

"Aggressive arrogant dismissal of anything except their exact view"

Man, if only someone around here had even if just an inch of self-awareness...

I think those guys both hate C++ so much that they want to dismiss everything about it instead of using all the features that work for them like most people.

My interpretation is that games want to allocate large pools of resources, like GPU buffers, cpu memory, etc, and reuse that memory over and over. Ie: Reinitialize it.

They might say this, but there isn't a good technical rationalization here since anyone can create a global data structure just as easily in C++.

Exactly! The most interesting arguments for Rust that I've ever read have come not from people who hate C++, but precisely from people who are really into C++, because those are the people who actually recognise its shortcomings are are better positioned to give INSIGHTFUL criticism into it.

People who are motivated by a superiority complex or by the wish to impress a herd of twitter followers hardly if ever produce a thought I want to read.

Hate C++? They both use(d) it...
I didn't say they don't know it.
As opposed to using C, Rust, or something else (Jon eventually made Jai but Casey still uses C++). Obviously they don't outright hate it.
They talk about hating it all the time and using some minimal subset because they have to, but the point here is not what these two internet personalities hate, it's that there isn't a technical rationale against being able to use destructors. They are hugely convenient but also completely easy to avoid if someone wants to.
Everyone uses a subset. Bjarne himself says he uses a subset.
I also use a subset but the point, which I already preemptively explained, is that they don't have a technical explanation for ignoring this part of it.

It just seems to be wholesale aggravation so they want to reject things without a sound reason.

If someone asked me why I avoid inheritance I would go on a well thought through rant that connects real problems to the pragmatic reality of what that feature brings.

See my history regarding my point of view on C or Go.

Yet if my job requires to use C or Go, I like my paychecks, have bills to pay and being proud doesn't pay bills.

Same thing with them, having to use C++ at work, doesn't mean they are big fans of it, indeed the very reason Jai exists is for Jonathan Blow never to use C++ again on his projects.

Casey spends a lot of effort on what he considers an anti-pattern where you're making a huge number of separate objects. I think a lot of this comes from Java, a language where all the user defined types actually are obliged to be heap allocations. If I make a Goose type, and I say I want a Goose, Java will allocate space on the heap for the Goose and put my Goose there, that's really how Java works. If I make a growable array of them ArrayList<Goose>, and add each of 500 geese, that's 500 allocations for geese plus maybe 8 allocations for the ArrayList, so 508 total. Ouch. If making a Goose was itself cheap this overhead hurts badly.

But a lot of languages aren't like that, and so this doesn't translate. Obviously in Rust with Vec<Goose> that's only 8 allocations, each local Goose lives in the stack - or, if you knew up front there were 500 geese, you Vec::with_capacity(500) and it's a single allocation - but similar is true in many languages, Java is an outlier.

Java doesn't mandate the usage of a heap - it can in certain cases avoid doing so and allocate on the stack (escape analysis).

Besides, as mentioned java's allocations are much closer to something like using an arena in a low-level language, then a "slow" malloc. It uses thread-local allocation buffers, where you have a large buffer with a pointer pointing to the start of the free region. Allocation is just a pointer bump, not even needing synchronization since it is per a single thread. As it gets full, the GC moves out still alive objects in the background and resets the buffer.

This is pretty much the most efficient one can be after per-object type arenas and simply not allocating.

You're correct that by an "as if" rule the JIT may in some cases be able to do this trick, but the specification says you're getting a heap allocation and so in most cases, including the example I gave that's exactly what happens.

It doesn't matter that you say this is "much closer to something like using an arena". It's definitely work that we needn't do at all and that's AFAICT that's how Casey ends up over-correcting so badly.

> This is pretty much the most efficient one can be after per-object type arenas and simply not allocating.

Sure. "A C is pretty much the highest grade one can get in this class, after grades A and B".

What "work" do we do needlessly? It's a pointer bump. With reclamation being no work. Comparatively a malloc call will have to do extra work to defragment.

Also, a stack is not a separate hardware element of the RAM, it's not faster than a sufficiently hot part of the heap - it just so happens that the current stack frame is pretty likely to be in cache.

> What "work" do we do needlessly? It's a pointer bump

Any work here was needless. There was no need to do work.

On the allocation happy path there is literally less work in case of Java. Like at least try to get what I'm saying and argue with that - I'm not saying that Java is faster or whatever, but that it has chosen different tradeoffs, and "stack vs heap" is an oversimplified model that doesn't help us understand the real performance impacts.

A pointer bump is a pointer bump, vs a malloc call that will try to find place, do some housekeeping, etc. And your Vec will need at least a single allocation, the backing buffer is not on the stack.

> On the allocation happy path there is literally less work in case of Java

Doing more allocations isn't "less work". No matter how many times you mumble "But it's just a pointer bump" the alternative was no work.

> it has chosen different tradeoffs

Sure, it's easier to implement this, the New Jersey style. But that's not a benefit to anybody else, so if you're not Sun Microsystems (which you aren't, Sun no longer exists) then that's not actually a benefit at all and this was a bad trade.

Which is why people have expended an eyewatering amount of effort on Project Valhalla to some day fix this stuff.

Actually, this is my personal opinion, but in Java, allocating an object is just bumping a pointer, so it's much cheaper than doing a malloc in C. I personally don't think the stack is a good place to put temp values and I'm sure neither does Jon Blow, considering he put temporary arena backed allocators right in the language.
Having an arena allocator is completely unrelated. It's one of the features all of these languages out of the Handmade Community seem to have, including Odin (the subject of this thread) and Zig. It's not useless but it's a weird niche to target first.

Local variables don't somehow live in this arena allocator in Odin or Zig, and presumably (I haven't used it) not in Jai either. The obvious place for a local to live is the stack - unless you're Java and you need all your user defined types to live on the heap so that they have unique identities. So that's where locals live in Rust, Zig, Odin, C, C#, Go ...

They might've pushed it, but they certainly didn't invent the idea. Although I'm not 100% familiar with their arguments.

POD - objects with no behavior are pretty popular way of representing data, you can serialize and deserialize them, etc. They have a pretty big caveat in non-GC languages - if you have an object with something like a dynamic array in it, suddenly your stateless POD becomes stateful, and needs RAII otherwise you get a memory leak. No idea how they solve that without GC.

Yeah, that's weird. I first learned about RAII from professional game developers at gamedev.net.
Games ? Many have talked about it, but many also make their games work inside of Unity and so on, so, depends on the project.

My angle is systems programming, and there, it absolutely matter. If you are performance sensitive, then you try to avoid crossing the user-space -> kernel boundary more than you have to.

Eg, ask for lots of memory, manage with arenas.

Interestingly Odin and Zig both lean into this heavily. Rust went a different route but has tried later to bolt on pluggable allocators.

> My angle is systems programming, and there, it absolutely matter. If you are performance sensitive, then you try to avoid crossing the user-space -> kernel boundary more than you have to. Eg, ask for lots of memory, manage with arenas.

This gives the misleading impression that ordinary memory allocators are materially different from arena allocators. They aren't. Both types of allocators first ask for a big block of memory from the kernel, then dole that memory out in userspace. There's no need to cross the userspace/kernel boundary more often than you need to, especially when you consider that you can replace the standard platform allocator with whatever you want.

To wit, C doesn't emphasize arena allocation anywhere near as much as Zig et al do, and yet nobody alleges that C is somehow less suitable for systems programming than these languages. Have you considered why that is? Because, for the most part, arena allocation doesn't make a significant difference, and in the places where it actually does make a difference, you can trivially build an arena allocator on top of the standard allocator.

Agreed. IME the main reason to choose arena allocators is for correctness, not speed. They make similar time/space tradeoffs to garbage collectors in that they grant higher allocation throughput in exchange for more memory usage.

The perf argument against RAII is very abstract and is less "RAII causes bad performance" and more "the kind of design that leads you to reach for RAII is the kind of design that's bad for performance." There exist similar hand-wavy arguments against many other C++/Rust features.

More generally, "you shouldn't even want that" is basically a meme at this point in programming language design. Every new-ish language has some version of it.

Correctness (the kind that comes from simplicity and maintainability) and performance pretty much always go hand in hand. The only additional ingredient to "bridge the gap" between the two is detail.
> the main reason to choose arena allocators is for correctness, not speed. They make similar time/space tradeoffs to garbage collectors in that they grant higher allocation throughput in exchange for more memory usage.

I'm not quite sure, do you mean arenas are slower, and use more memory, compared to standard vector-style allocation (which does a lot of copying, iterator invalidation, and wastes like 0.25x memory on the average), or compared to individual malloc/free (which does a lot of book-keeping work and wastes book-keeping memory)?

The difference between an arena allocator and RAII deallocation is that the latter is eager. This is the whole point: the problem that an arena allocator attempts to solve is to presume that fine-grained freeing is slow and thereby solve that by batch-deallocating a whole lot of things at once. But it will, be definition, end up using at least as much memory as an eager deallocation scheme, because the whole point is to not be eager, which means not immediately freeing memory even when you could. This is a fundamental tradeoff. I'm not sure what you mean by "vector-style allocation", but iterator invalidation et al is a non-sequitur for this discussion.
Iterator invalidation is absolutely a huge deal for many datastrucutres in practice. From an ergonomic standpoint alone it makes a huge difference. 10 years ago I argued the opposite, but juggling indices simply is not fun. Replacing pointers with indices can be useful but it's not a good default.

The important difference as far as I'm concerned is that the former is stack shaped and there is structural support for batched deallocation, requiring no cleanup code at all. RAII does not give you that at all -- it is just a disciplined system to automate some code, but the complexity is absolutely still there, and a lot of constraints are put on the types and your internal organization in order for RAII to work.

Arenas are quite a good fit for frame memory in graphics programming, and they absolutely do not waste memory there if done right. On the contrary they probably save some, but that's not the point here. We're typically talking about a couple megs per frame at most, which is spare change.

Some people, like Ryan, have managed to apply arenas more widely, but personally I am more for a varied mix, and don't mind a bunch of manual code either, but I definitely like to control allocation more closely compared to what e.g. malloc/free allow. I like to keep objects in their specific subsystems. I have written my own block allocator to do this, and it does not show up on my profile like almost at all, it's <<< 1÷ of my CPU usage, also syscalls are zero after the first frame.

As I said elsewhere, performance and correctness/clean modelling always go hand in hand. The correct model must allow for the right level of performance, or else it's not the correct model.

Look up a few comments, I do systems programming. I am aware, you are barking up the wrong tree, friend.

That said. You asked about C, which I use for my job. Most every large C code base end up abandoning the stdlib (such as it is) and inventing their own. Since they do, we aren’t as hurt by abandoning it as you would be in e.g. Rust - the rust stdlib is useful, the C stdlib is.. not great.

Once you abandon the stdlib, a likely first stop is writing your own routines for allocating and freeing memory. There are different approaches here, from glib’s or sqlite’s alloc and free routines to people writing an allocator abstraction (basically a struct with a vtable for allocating/realloc/free) and when you build your own “stdlib” around this abstraction, you are fine.

As for why you may want arenas vs other allocation strategies, that again deals with how often you are comfortable going across the user-space/kernel boundary and how clever you can be with your allocations or how much internal fragmentation you can accept.

As with all other stuff, it depends. But arenas are often great when you can assert that a series of objects share the same lifetime (death time, rather). In these cases, your amortize the syscall cost, have nearly no additional work to manage the memory (contrast to e.g. the complexity of jemalloc) and can free a series of objects in constant time.

Rust barely even trying on the pluggable allocators (seems like it's never going to land, afaik "allocator-api" has been sitting proposed in nightly only since 2018) is one of the things that frustrates me (fulltime Rust eng) about the language and makes me feel like it's just a language that's been eaten by web services developers, applications level work, and/or tokio, and isn't "serious" as a systems PL really despite the verbiage.

Building a database, operating system, etc. absolutely requires fine tuned control over allocation. You can get around some of these things in Rust, but it will fight you. You'll effectively have to turn your back on the containers in std and build your own vectors, maybe even your own Box, etc.

Odin looks really appealing to me at one level, but I'd have a hard time switching to any language at this point that doesn't have a borrow checker story.

Did you try Swift? Its interoperability with C (and even C++) is great IMHO.
It's GC
Pedantically I’ll say it’s reference counted, and someone else will say that’s still a from of GC and I’ll just save us the mini-thread.

Reference counting has deterministic timing, you can run a deconstructor without registering objects for deletion and running any known finalizers (what you need to do in all GC langs I’m aware of.)

Yes, but only classes are GC (refcounted as parent comment says, indeed). structs and other elements are not.

You can even use the same ownership model as rust (borrowing et al.) with non copyable types.

> Reference counting has deterministic timing

Define “deterministic timing”.

- One object going out of scope may mean calling free once, but it also can trigger calling free billions of objects, even for the exact same object

- Even freeing one object can have largely varying running time, e.g. to coalesce free blocks or, because it happens to be the last block in a virtual memory region, to unmap a block of virtual memory, blocking the program potentially for an arbitrary time

- With garbage collection (as with reference counting), lots of the overhead of objects going out of scope can be moved onto a specialized thread.

> you can run a deconstructor without registering objects

Not requiring finalizes does make reference counting easier, yes. The downside is have to store the reference counts somewhere, and keep them up to date (enough)

Well, GC has indeterminate timing in and of itself. Reference counting does not. It can still trigger indeterminate timing if the destructor calls free, or if the "thing" going out of scope is a pointer, but that's on the destructor or the pointer, not on reference counting per se.
[delayed]
I think I understand you now. You don't do reference counting for something "on the stack" (in C++ terms), you only do it for something on the heap. Things on the stack can have destructors, but that gets called when the stack frame goes out of scope. So anything reference counted, when the count goes to zero, will not only have its destructor run, but will also be deallocated on the heap (calling free).

And then whether you blame that on reference counting or garbage collection or heap allocation is kind of an arbitrary distinction.

OK, I get it. You were right :-)

"- One object going out of scope may mean calling free once, but it also can trigger calling free billions of objects, even for the exact same object

- Even freeing one object can have largely varying running time, e.g. to coalesce free blocks or, because it happens to be the last block in a virtual memory region, to unmap a block of virtual memory, blocking the program potentially for an arbitrary time"

So, like C, C++, or anything else that you wouldn't call garbage collected.

If the major obstacle for adopting Rust is C interop, you may find my project CO2[1] appealing. It helps you to define C crates, `#include` C headers, while exporting a Rust API with Rust types in your crate boundary.

[1]: https://github.com/hkalbasi/co2

I've been using Odin for about 6 months now, and to be honest, it's hard to find fault with it. I've used it for STM32 microcontroller firmware, web and desktop applications, and all are performant and compile quickly.

My one issue is (and I'm fully aware it will never happen) I do wish there was some sort of first-class solution to inheritance. I've grown to love procedural programming, but some problems really are just better solved with a more OOP approach. Just because classes exist does not mean they need to be used.

But as far as a language to "get stuff done" with as few tradeoffs as possible, Odin is about as good as I can imagine a language being.

I'd like to hear more about Odin + STM32 MCU firmware, do you have any good resources? I'm also curious how difficult it may be to get work with it on ESP32 (ESP-IDF) / RP2350.
Out of curiosity:

In your opinion, could a minimal system to develop in Odin be squeezed into a device like the one(s) you targeted?

That's assuming maybe some tweaks to the toolset, doing without some niceties, but not cutting core features out of the language.

Asking 'cause I have a passing interest in programming languages that allow for native development on really small implementations (think sub-1MB on bare metal). The list of candidates doesn't seem long.

Odin is not like JS or something where you'd need a VM or transpilation process to target an embedded system. It's just C with nicer syntax and modern data structures, there's no "squeezing" required. You just compile for the target you want to run on.

Here's a UI framework, if you scroll down you'll see it on a Raspi Pico: https://github.com/MadlyFX/Ansuz

I think you missed "native development". I'll rephrase:

Could a toolset to develop in Odin be made to run on (not just target) an STM32 microcontroller like you used?

> It's just C with nicer syntax and modern data structures

That suggests the above would (in theory) be possible for any device that's roughly in the same class as "can run a C compiler". Correct?

> It's just C with nicer syntax and modern data structures

it's not, it's llvm

Okay, I mean obviously :D I was more trying to convey the feeling of it.
> STM32 microcontroller

The answer is "yes" depending on what you mean by STM32 :D

But if it's one we don't currently support officially, it should be pretty easy to support too, with probably a little extra assembly.

I’d be interested in reading about your experience building web applications with it. Last I looked, its stdlib didn’t have great support for that.
There’s an official http package coming out and native tls support as well.
Yeah, but OP has built web applications without those. So, I wonder how they went about it.
Personally, I have a certain Turbo Pascal feeling while playing around with it, yep I am aware of the influences.
Like Go and Rust, the coolest thing (and maybe only cool thing) about this language is the name.

Now that LLMs are out of the bag, I expect to see a lot of new programming languages. In such an environment, one is better off promoting what you can do with it, rather than the language itself and its quirks.

Show me what you made with this language - it will help me better understand the use case(s) and trade-offs.

I say it because I see Odin following Rust in that it is relying a lot on its name in marketing. And on these microscopic language quirks that people don't really care about en masse, things people easily work around in other languages.

Doesn't seem very useful (sales-wise) to spend so much time talking about garbage collection or whether or not a language has native tuple support - and also the branding just doesn't matter that much. Go does a better job of this (what is Go's logo? Idk either. I know Swift's though. Go is 4x more popular - both "released" around the same time by similar companies).

JavaScript has the worst name of all time yet it's the most popular language because of how heavily relied upon it is by all kinds of consumer technology.

Python also - not a great name really, still wildly popular because of its use cases (and having a high quality library ecosystem).

Turbopack was a great example of when, why, where, to use a new, novel language (Rust) in an area I'd never consider it (web dev). It also opens my mind up to Rust doing other things like that in the ecosystem where I'd never consider it before. Now I kinda know what Rust is for.

This is maybe the best example I've found for Odin: https://odin-lang.org/showcase/solar_storm/

It opens up questions like "is it designed only for graphics and GUIs or is it more backend in general, is it low level systems or network" etc. then after that I'd ask whether or not it's strongly typed, compiled, and get into all those language quirks (which don't matter that much).

After seeing the game demo, I read that it wraps basically every major GL - more questions related to graphics programming but then the site keeps saying:

"The Data Oriented Language"

...on every page.

Up until now I had began to think it was a graphics oriented language, with all the GPU talk and game dev demos. But now it's a data language? And what does that even mean?

Then there's a book for sale that I keep running into. This isn't helping!

I love the name "Odin", and I like the idea of a new graphics-oriented programming language that somehow does something useful. Push me over the edge! Help me fall for this thing, it's so much like Rust in this way - seems amazing! But why? Why do I want this?

If it were me: I'd narrowly focus on 1 thing at first, something controversial and powerful - private MMO servers with good server-side physics and anti-cheat, or torrents, or running local AI models.

Build a beautiful IDE designed around your language ecosystem (that works for other languages too) and give it away for free. Optionally include a 7b LLM in the IDE to autocomplete Odin syntax to teach people the language. Get private servers for games like WoW and FF14 (illegal in most countries) online and teach people how to deploy their own. Tout the fact that no backend language in all of gaming can simulate physics to such a degree (fluid dynamics, etc.) and that no front-end language in gaming wraps all major GL providers so seamlessly in a single, easy-to-learn syntax.

Make the IDE icy af

Interesting, I am thinking/expecting we will see a massive decrease in new languages. Or people might make new languages but the will not get any adoption.

A new language now has to clear the ever growing hurdle of not being in the LLM training data.

Unless the language provides an absolutely incredible technical or runtime advantage over every other language that LLMs “speak” well I think it will really struggle to gain adoption.

Additionally, a language’s qualitative benefits to human writers arguably matters less and less.

I used to live in my IDE. Now I use it maybe an hour each day even in a JVM based language. IDEs dont really matter as much anymore.

> A new language now has to clear the ever growing hurdle of not being in the LLM training data.

I found this to be far less of a prblen than I thought it would be.

Do you have practical experience?

Not with a new language but novel approaches within a framework or paradigm feel outside the LLM wheelhouse.

Even if the LLM is adept at novel languages the developer still has to learn it and learning new languages now when most programming is done via a prompt-review loop feels like it has lower ROI.

I find they’re pretty good with Odin. Not as good as with Go, and nowhere near as good as Typescript, but definitely good enough.
Data-oriented is a programming paradigm, just like object-oriented and procedural and functional. It has nothing to do with Big Data. It's about the way things are done. It prefers something like an ECS (data-oriented) rather than a class hierarchy (object-oriented). "Graphics oriented" isn't a thing.

Also, i disagree with your point about "promoting what you can do with it, rather than the language itself and its quirks". Like, what? Every language can do everything another language can. As long as it's turing-complete and has some interface for FFI or something similar, it can do anything. You can make a full modern SaaS in C if you really wanted to, from backend to frontend. The language itself and its quirks are what would make you maybe consider not doing that (as much as I love C, that would just be stupid if your goal is anything other than fun and experimentation).

I can see all the great software and games that were made with C++. Doesn't make me wanna use it though, the language sucks.

Your paragraph about IDE and the whole name thing just seems very out of touch to me. Are you a marketing / HR / sales person perchance?

This goes for most languages but also a lot of things in life: if you see something and don't know why you'd want it, it's not for you.
You're thinking too much along the lines of "language x can or can't do y". See: Turing complete.

Ultimately it all boils down to machine code. Programming languages avoid the tediousness of programming in that, express program flow in a more concise / elegant way, and manage complexity for the project at hand.

How that's done, is a matter of taste, and how well the tools suit the job. Existing runtime environments (say, browsers with highly-optimized JS engine) are important here.

> Show me what you made with this language - it will help me better understand the use case(s) and trade-offs.

That's the spirit!

I wonder when we'll see new languages created specifically with LLM's in mind.
What would that look like?
I don't know but I would imagine there are a lot of inefficiencies in modern languages from an LLM perspective that it could strip out, reduce token costs, improve speed etc.
(comment deleted)
So, assembly?
[delayed]
There are already plenty of PLDI and SIGPLAN talks on LLM => Assembly, with some guardrails for determinism, instead of having a "classical" language as translation layer.
Sorry are we referring to the efficiency of generating programs (i.e. optimizing LLM development) or the efficiency of the output (i.e. optimizing software through the generation of assembly)?
An LLM-only oriented language doesn't make sense, because without human generated training data there is nothing for the model to learn from.

But if a human-oriented language were to be designed to also be better for LLMs, I think it would involve deeply expressive syntax that can succinctly but distinctly represent a very broad set of common operations. Succinct so context can be managed well, distinct so completions don't confuse one thing for another, broad so as much "reasoning" can be taken away from the LLM as possible. An anti-C. A new take on the goals of Java and Go to be languages that protect the application from the Junior Developers You're Likely To Hire.

I somewhat think it would also involve application state images ala Smalltalk. LLMs seem okay at generating small deltas. Many deltas sequenced together invites compounding error. LLM generated apps are unlikely to lead to common libraries being compentized and extracted out of the application to share with other applications; it seems like LLM code generation is already a "married to a specific project" act already. So, having a living state image might reveal some benefits by leaning into incrementally developing the application in situ, as a whole.

I would guess a good way to implement a language meant for LLMs would be to strip out as many features as possible, so that all programs needed to be composed of just a handful of common structures written in shorthand. As much compiletime enforcement as possible. Maybe human-crafted abstractions for more error-prone concepts like multithreading that limit how they can be used in the language but provide ample, testable bumpers.

It would be interesting to design some mechanism where an external actor could program extensions into the language easily, which then would become a new black-box tool that an LLM could program with. Ideally though, I think you want to remove as much agency in reimplementation and re-abstraction as possible from the LLM. This is really just some tier above current high level languages, with as much dumbing down as possible.

I'm not sure this is really necessary or would even be effective because there wouldn't be a million examples for the training data of an LLM. But I imagine something along these lines could make an LLM more productive and token efficient.

I think it would be better to approach it from the other side, the priority is not to design a language for LLMs but a language more suitable for humans to think with. And not a natural language like English, which is inconsistent and allows illogical formulations, but something like Esperanto or Interlingua (Latino sine flexione). Something that is based on mathematics and logic at the bottom, like Lean, with enough abstraction layers for a person to be able to "speak" with the machine intuitively.
The way forward will be more focused on formal specifications kind of approach, and there are already a few attempts at it.
I wonder if this will help them get a wikipedia page. (not sure whether this comment is a joke or serious...)
It would, but the book has existed for a while now, which is a good thing for anyone looking to learn Odin. The author, Karl, has had time to polish and update the text. He has his own Discord and is also available in the Odin Discord and makes helpful posts there as well.
Kinda wanna know why I should learn this language. Unfortunately there’s no wiki entry (deleted with some controversy abut notability), so it’s hard to get the gist of it.
Odin overview is great resource for getting a quick gist (and learning the language as a bonus): https://odin-lang.org/docs/overview/

But mainly, language doesn't have a special gimmick. The main idea (imo) is it is opinionated to have defaults to cater for majority of the cases. So once you get used to it, it is pleasure to write C-like code in it.

Promoting here on HN is not usually allowed. This is a paid tutorial for some obscure programming language.
I have no connection with the book author. Also, people post all the time links to paid sources on HN (like books, software or pay for services).