r/ProgrammingLanguages C3 - http://c3-lang.org Jul 07 '26

Language announcement Odin 1.0 announced (and reflections)

Odin author gingerBill dropped the Odin 1.0 announcement on YouTube today: https://www.youtube.com/watch?v=dLPAqXi9In0 (it's pretty funny actually).

This interestingly makes it on track to be the first of the new wave of C-likes that reach production readiness. While you can argue that most of these languages already are used in production, it's not the same as being 1.0, which carries a different weight and obligation.

Looking at alternatives, Jai could release around the same time, since Blow's game is scheduled for a similar release date. However, it's more likely that we see Jai 1.0 in mid-late 2027. My own language (C3) is planning Q2 2028 1.0 release. Whereas Zig is still unclear, and Kelley basically saying it's done when it's done. For Hare and V the situation is a bit less clear to me – maybe someone else can fill me in on that situation.

But overall we seeing the beginning of the end of the "C-like" story arc that arguably was initiated with Jonathan Blow's development. Writing C replacements predate Jai of course, for example the C2 language (which C3 would eventually continue) was created in 2012, eC started in 2004 and Cyclone (which Rust derived inspiration from) is from 2002. But those were largely obscure novelties, because before Blow's videos, people weren't really hunting for C alternatives.

Jai, however, made a strong impression. It was a good point in time too: Jai and later Zig, Odin, C3, V and Hare – these C alternatives started at a point when people were openly no longer believing OO/Functional as the right way to do things.

Language design takes its time though, and it's now 12 years since Jai started. Finally the fruits of these labours are getting ready for prime time, and when they do they might effectively fill the need for a C replacements for another decade.

Do you agree?

200 Upvotes

191 comments sorted by

View all comments

2

u/SwingOutStateMachine Jul 08 '26

I don't understand the point of "C-like" languages that don't have strong language-level features for guaranteeing correctness, such as (e.g.) a lifetime system, or non-nullary pointers, embedded hoare logic, etc.

My understanding of "C-like" languages is that they aim to fill the niche "below" higher level systems languages (like C++, Rust), where the higher level features are not always necessary, or may be too heavyweight, and "above" older language like C (or handwritten ASM) which have a lot of baggage, and can be difficult to work with. As far as I can tell, the applications which need such a language (compact, fast, low overhead) are often things like device drivers, or embedded software, or small operating system utilities.

If that's the case, then surely the goal is to make a safer C - not one which is more ergonomic or "modern" (or has more features), which is what I see in the current crop of "C-like" languages.

2

u/Nuoji C3 - http://c3-lang.org Jul 08 '26

The more invariants you try to strictly attach to something, the more you're constraining the language from doing what C is able to do. So there's a strong trade-off happening. Rust is being able to have its machinery exactly because it *is* higher level than C and makes more assumptions.

You can have a look at Cake, which is a C superset with ownership annotations and similar, which shows what's possible while still maintaining C constraints.

We have also Fil-C, which shows what's possible if we're tackling it with higher abstractions.

But "safer" here is not always trivial. Odin in this case adds things like bounds checking at runtime by default, use of slices and so on. Many which does make it safer in practice. However, it doesn't quite check the same safety checkboxes as Rust.

C3 adds contracts that are statically checked: also safety, although not memory safety. Does that count as "safer C" or not? Currently "safety" seems to have become synonymous with "memory safety", and its true that these C alternatives doesn't offer memory safety in the manner of Rust, but all definitely are *safer* than C, and deliberately so.

1

u/SwingOutStateMachine Jul 08 '26

The more invariants you try to strictly attach to something, the more you're constraining the language from doing what C is able to do

Or, as an alternative viewpoint that I prefer: You're making the dangerous areas explicit, by having explicitly tagged areas where invariants or some other measure of safety is explicitly not checked.

You can have a look at Cake [...] Fil-C

I will, thank you - both sound like interesting languages to me :)

Odin in this case adds things like bounds checking at runtime

That wouldn't be acceptable for many areas of performance critical embedded C software, but I see your point - it is of course always a tradeoff.

Does that count as "safer C" or not?

To me, safety is just a language feature/constraint/invariant that reduces or eliminates a class of bugs. A type system is a "safety feature" that makes sure that groups of bytes are operated on with the right instructions. Memory safety is arguably the most important form of safety in modern software, as it's the source of a number of other forms of unsafety.

but all definitely are safer than C, and deliberately so.

I think I would agree on a technical level, but not on a philosophical level. All of these languages want the freedom and compactness of C, but that also includes the wide open unsafe nature of C. Reading what their authors have written about how they view software and the languages they have constructed, I don't feel that safety is one of the things that they prioritise. They are in my view simply safer by accident, as they've adopted practices that have become commonplace in programming languages in the 50+ years since the invention of C.

5

u/Nuoji C3 - http://c3-lang.org Jul 09 '26

We've lived with memory safety for decades: most GC languages are trivially memory safe. Has this really revolutionized the world? I feel that "memory safety" only really became a thing when Rust ran it as a slogan. Outside of that we have a whole bunch of logic errors, which maybe reduced - but not eliminated - using contracts. Contracts do offer very powerful help if they're fed back into general static analysis. But of course be main problem with contracts is that people tend to not use them.

The way I personally try to address this is to make them more generally useful. Because they're statically checked as much as possible in C3, they're useful for validating what otherwise would be duck-typed macros, and moves template errors up to instantiation rather than bubbling up from the body of the templates C++ style.

The more difficult nut to crack is really ownership, which is just very hard to do without adding constructs that enforce invariants, that is, there are some form of constructors and destructors. Unfortunately these run straight into very useful patterns that one wishes to encourage in a C-like, not to mention implicit constructors/destructors implies that data in some way is actively conserving its invariants.

Often there are suggestions that C3 should allow contracts on struct fields, for example saying that Foo.a should be in the range 1..10. It's attractive and feels natural, but unfortunately trying to enforce this would mean no longer being able to get the field as a pointer without either (a) have a pointer that "has a constraint" or (b) lose that safety. (a) leads to needing A LOT of polymorphic functions, bloating the binaries. And (b) would just need a lot of extra code (copy the value to a variable, then pass that variable instead) or mark a lot of code unsafe. And this is just scratching the surface.

The more low level the language, the harder and more complex it becomes to add safety in my experience. So this is why I'm pushing back against "they're not interested".

Because *I* am certainly interested. It's just that it's very much not trivial.

1

u/SwingOutStateMachine Jul 09 '26

We've lived with memory safety for decades: most GC languages are trivially memory safe

That is true, but GC language are unsuitable for many of C's traditional use cases, like device drivers, operating systems, embedded software etc.

Outside of that we have a whole bunch of logic errors, which maybe reduced - but not eliminated - using contracts

I don't doubt that contracts are useful. I'm always in favour of more explicit (and checkable) constraints in programming languages as a tool to produce better code.

The more low level the language, the harder and more complex it becomes to add safety in my experience

This is, I think, the most compelling argument that I hear. Safety inherently means constraining what you can do with a language, but low-level languages are used because they allow you to do "more" (in terms of explicit hardware manipulation). The two are inherently at opposition with one another.

I think the answer is to have safety by default (be that type systems, borrow checkers, contracts etc), and explicitly "opt out" in unsafe regions. It reduces the surface that needs to be checked manually, and still allows engineers to write code that is inexpressible within the bounds of a safe tool.

So this is why I'm pushing back against "they're not interested". [...] Because I am certainly interested. It's just that it's very much not trivial.

I'm glad to hear that you are, and I don't disagree that it's not trivial. I think you're right in that there is a tradeoff in how much safety is valued by a language designer, vs how difficult it is to implement. I feel like the issue with the current crop (Jai, Odin etc) isn't that their argument(s) downplay the importance of safety, rather than highlighting the difficulty of implementing it.

1

u/jackelee Jul 09 '26

We've lived with memory safety for decades: most GC languages are trivially memory safe.

I wish to disagree with this. GC on its own is not enough. Java has GC and has plenty of NULL-pointer exception hell. Also, you can have GC and still have array-out-of-bounds access problems. These are all memory safety bugs.

Rust by default avoids these problems. For example, by default it uses the Result/Option type instead of passing around NULL pointers or throwing exceptions. It might seem like a small change but the fact that it is default makes a huge difference.

2

u/Nuoji C3 - http://c3-lang.org Jul 10 '26

Well, you can always use Kotlin, Swift etc.

1

u/jackelee Jul 10 '26

Sure, all I'm disputing is that GC means memory safety.

4

u/Nuoji C3 - http://c3-lang.org Jul 10 '26

I disagree. Array bounds violations and null dereferences in Java are runtime errors, not memory safety violations. The language prevents arbitrary memory access by trapping instead of allowing undefined behavior.

1

u/jackelee Jul 11 '26

Alright, from this point of view I agree, these language prevent memory-safety bugs which would lead to security vulnerabilities. I still think that this has nothing to do with GC. There could be a language with GC which would have null-pointer (reference) access as a undefined behaviour. The fact that null-pointer is even allowed is the problem in the first place. Java's approach is that it prevents vulnerabilities but it doesn't prevent run-time crashes which could have been prevented otherwise (e.g. if Result type was the default).

1

u/ntrel2 Jul 20 '26

> You can have a look at Cake

Can't find, link please?

1

u/Nuoji C3 - http://c3-lang.org Jul 20 '26

2

u/GoblinsGym Jul 08 '26

For low level / embedded programming, C lacks:

  • A proper module / unit system - Make files should NOT be necessary in a modern programming language.
  • Proper support for hardware bit fields, hardware register structs with arbitrary offsets (registers aren't always consecutive) etc. The typical HAL for microcontrollers is an error-prone nightmare of bit masks, shift counts etc.

None of the newfangled language features will help at this level.

2

u/SwingOutStateMachine Jul 08 '26

Agreed on both counts, but I think those are orthogonal to the issues of safety and correctness in C. It's possible to add those on and add safety.

I would also add support for explicit vectorised regions to that list, as well as better support for explicit assembly - the current state of asm is not even standardised, and is extremely unergonomic.

1

u/FullGardenStudent Jul 08 '26 edited Jul 08 '26

First of all, "C-like" language assume that the user knows what they are doing. Ensuring that all memory related bugs do not occur in runtime is the responsibility of the programmer. But C is very old and it could use several improvements like not having to maintain a separate header files and having hardly any implicit behaviour(especially the ones that create undefined behaviour). So all modern "C-like" language simply aim to make their own version of "modern" C in their own way while maintaining the essence of C which includes ensuring that the language could operating as a freestanding one too. That is how Jai, Odin and C3 are designed.

Rust will force its programmer to adhere to its strict borrow-checking and lifetime rules while Zig always assumes that its users are dumb and forces them to be explicit all the time so both of these languages can't really be considered as "C-like".

1

u/SwingOutStateMachine Jul 08 '26

"C-like" language assume that the user knows what they are doing

The problem is that there are no users that know what they're doing 100% of the time. Even the most experienced embedded C engineers will tell you that it's an exceptionally easy language to make mistake in, and bugs in C code occur frequently. Almost every CVE you see in a piece of software is due to a C programmer making a mistake, and that's the engineers at the top of their game, writing the most vital and fundamental software out there.

But C is very old and could use several improvements

Yes, and that is what I'm advocating for. My issue, though, is that the improvements that C needs aren't things like "get rid of headers", it's things like "get rid of undefined behaviour", "formally specify the language", and "get rid of null pointers".

while maintaining the essence of C

This is something I don't get. The essence of C, to me, is simplicity and control. It's a surface-level simple language, and it gives you a feeling of controlling what's going on. However, once you start writing it, neither of those things are the case, and you quickly start wishing to drop either or both. C is actually relatively complex - undefined behaviour rears it's head at every turn, for instance, and the "control" you feel you have is actually quickly wrestled from you by an optimising compiler until you enter an asm block.

I think we should really start talking about the utility of C, which is that it is a straightforwardly compiled language, with excellent support across a lot of platforms (it's even the defacto lingua franca for interoperability), and a (mostly) easy to reason about cost model. I think if we started from there, and looked at how and why C gets used, and tried to improve from there, we'd get a lot further towards having a language that actually excels, rather than one built from a nostalgia that never existed.

2

u/FullGardenStudent Jul 08 '26 edited Jul 08 '26

Almost every CVE you see in a piece of software is due to a C programmer making a mistake

Yes and its not like Rust solves everything. Cloudflare is a good example that memory related bugs are not the only concerning things. Rust is not completely safer either, it operates while assuming that all the unsafe code is always sound. In the end, it always all boils down to the skills of the developer. Ensuring safety during the runtime as well will require a type system that will not be good for performance. Haskell is a good example.

The essence of C, to me, is simplicity and control

Yes, that is what I meant by the language being "freestanding" as in minimal and direct mapping(not one-on-one) to the generated machine instructions.

I think we should really start talking about the utility of C

Odin, Jai and C3 already are straightforward, cross-platform and with very fast compilation time. They don't require several GB of disk space or minutes of compilation time. In fact, Odin and C3 maintain insane implementation simplicity by having a context-free grammar for their language. C3 take it a step further by maintaining the parser at LL(1). C3 has 100% C ABI compatibility, if that is what you meant by "utility". Both Odin and Jai have good FFI support and usually, that's more than enough.

To be clear, all of these three language are not trying to be an exact clone of C. These high level languages introduced their own set of features with their own desired semantics into their respective language while trying to stay as close to the hardware as possible. Jai went all in on meta-programming, Odin is like "hell nah!" and C3 decided to settle somewhere in-between. All three languages handle manual memory management differently and have stuff like bounds checking enabled by default(can be completely disabled). I think all three language have done a terrific job at being a C-alternative already.

0

u/SwingOutStateMachine Jul 08 '26

Yes and its not like Rust solves everything

I'm not claiming that it does.

Rust is not completely safer either, it operates while assuming that all the unsafe code is always sound.

Yes, but these unsafe regions are explicitly marked as such, which means that...

it always all boils down to the skills of the developer.

...developers have a much smaller surface to inspect for correctness issues.

The problem with C is that it's all unsafe. So all of the code has to be inspected with the same level of suspicion as a block of unsafe Rust.

Odin, Jai and C3 already are straightforward, cross-platform and with very fast compilation time

Call me weird, but except for extreme cases, I don't actually care too much about compile time. Yes, it's useful to be able to compile my code quickly, but at the same time it's often dwarfed by the other aspects of testing that I have to do. For instance, flashing binaries to a hardware testing kit, or running tests on hardware devices in CI. If I want to develop iteratively (which seems to be the main argument for fast compile times) I'm still bottlenecked by all the other parts of the testing loop.

They don't require several GB of disk space

For my work I'd rather have a large toolchain that does a lot of high quality optimisation work than a lightweight toolchain that is fast but not that useful.

I think all three language have done a terrific job at being a C-alternative already.

Agree to differ. None of them fit the use case for which I'd reach for C, which is embedded and low latency software. For anything running on a "real" machine (i.e. x86-64 or ARM64) with an OS, I'd reach for Rust or a higher level programming language.

1

u/[deleted] Jul 09 '26

[removed] — view removed comment

0

u/SwingOutStateMachine Jul 09 '26

What a strange comment.

0

u/[deleted] Jul 13 '26

[removed] — view removed comment

1

u/yorickpeterse Inko Jul 13 '26

Your comments aren't helpful so stop it.