This is an especially hard ask, given how useful the FP-like features of Rust are, and I find it almost impossible to live without them. Basically, I am looking for a middle ground between C/Zig and Rust.
One such language I have found is Austral[0]. What other such languages are there?
[0] https://austral-lang.org/
I absolutely adore Ada [1], but the Pascal-syntax isn't for everyone.
I haven't used it yet, but have been hearing rumblings about Odin [2] a fair bit in these kinds of discussions. I tend to avoid too many symbol-heavy langs, but it's probably still less than Rust (I use a screenreader half the time).
Not saying you in particular, but it's weird, how too many won't tolerate running into some problems when they knowingly chose something still in beta. "Oh my, there was an issue with the beta software I'm using".
Maybe why JBlow refuses to release his beta to the public. Actually, it can be a good thing for the language, by people filing bug reports (so they can get fixed) or to do a PR (if they are at that level). That way, the language has more vetting, from more eyes on it.
(I have no idea what the current state is)
As for concerns... The main developer is a concern. Hard to trust them to support the language well, with some of the... Well, tantrums. This isn't aimed at a personal attack. But it is very hard to describe their responses in another manner.
This [0] thread on HN covers some of all of the above.
But, probably also important to point out that V and its drama have had dang threaten to ban the topic altogether [1]. There's a lot of drama.
See this article and the discussions, and if you’re so inclined follow along links in that thread from last time that show folks from the vlang team flaming all over HN.
> But I've had a few issues with the safety aspects of the language. Breaking through the checker isn't that difficult.
What do you mean? Can you "break through the checker" outside of unsafe blocks?
But there was a time where autofree was just a compiler flag and wasn't actually implemented, even though the author claimed that it was.
[1]: https://justinas.org/the-bizarre-world-of-v#memory-unmanagem...
Better to review more balanced and neutral takes on V[1][2], where the reviewers are not just stirring up drama with their community or funneling traffic to their site.
[0] https://github.com/vlang/vinix
[1] https://www.youtube.com/watch?v=puy77WfM1Tg (Anthony GG reviews Vlang)
[2] https://www.youtube.com/watch?v=dmVKerNY-fQ (Mike Shah reviews Vlang)
Just remember that even if writing memory-safe programs is your goal, using a memory-safe language is just a means to that goal, and even Rust isn't really memory-safe. Many Rust programs try to achieve memory safety while using a non-memory-safe language (be it C or unsafe Rust), and there's an entire spectrum of combining language features that offer sound guarantees with careful analysis and testing of unsafe code to achieve a safe program. On that spectrum, Zig is much closer to Rust than to C, even though it offers fewer guarantees than Rust (but more than C).
This is primarily an educational exercise to see how people find compromises that work for them, and languages in the same space as Rust using alternative strategies.
Is this something you experience when writing your code, or is this something you experience when reading other people's code?
If it's the former, I'd really love to hear more about those experiences.
If it's the latter, are there particular features that crop up that make code feel like too much?
(To be clear, Rust isn't perfect for everyone, despite our best efforts. And if you want to work with another language, you should! I'm not looking to defend it; your experiences are valid. We'd love to make Rust better, so I didn't want to miss the opportunity to ask, because we so rarely hear from people in the intersection of "I love Rust" and "Rust is too much".)
But even for educational purposes, using a language with a poor selection of libraries is likely to lead to a bad experience if what you want to produce is working, non-trivial software. Every project includes some "boring" aspects -- such as parsing configuration and data files -- that you won't necessarily enjoy writing from scratch. The overall programming experience is shaped by much more than the design of the language alone.
UnsafeCell I find particularly useful to exponentially decrease the amount of boilerplate needed to convince the compiler that your code is safe.
Yeah, and this points at a deeper issue: the concept of a language being either (binary) memory safe or not does not really make sense. Memory safety is a spectrum and most GC'd languages are safer than Rust (but you won't see a lot of "memory safety" proponents acknowledge this).
Also, there are mitigations that can make "unsafe" languages as secure as something like Rust, but those are deliberately overlooked and hence languages like Zig are constantly bashed using security as the reason.
[Heavy citation needed]
Rust isn't memory safe if and only if:
- You messed up writing safe to unsafe interface (and forgot to sanitize your binary afterwards).
- You tripped one of the few unsound bugs in compiler, which either require washing pointers via allocators or triggering some tangle of traits that runs into compiler bugs
- Somewhere in dependecy tree someone did the one of other things.
That is the definition of a language not being memory-safe. Memory-safety in the language means a guarantee that the resulting program is memory safe, and that you could not mess it up even if you tried.
Taking that to the extreme, it's like saying that a C program isn't memory safe only if you mess up and have UB in your program, something that C program must not have. But C is not a memory safe language precisely because the language doesn't guarantee that. My point is that there's a spectrum here, the goal isn't memory-safety in the language but in the resulting program, and that is usually achieved by some combination of sound guarantees in the language and some care in the code. Of course, languages differ in that balance.
C++ would be great if people didn't use C style de-reference or arrays, used smart pointers, and avoided "turing complete" template bullshit. It ironically would be almost as memory safe as Rust.
On the contrary, you can also make C very memory safe by avoiding a few patterns.
A programming language is always going to make some kind of compromise; better at some things, worse at others.
Simplicity/power and safety pull the design in different directions.
> Beautiful is better than ugly.
> Explicit is better than implicit.
> Simple is better than complex.
> Readability counts.
What really sets Zig apart from the usual suspects (C, C++, Rust) is that it has first class compile-time reflection capabilities built into the language (comptime, @field, @typeInfo, etc.) rather than bolted on as macros or templates.
Funny you make that analogy. I remember back when the two contending C alternatives were Zig and Nim, with Nim being syntactically almost a Python clone.
It seems Nim has gone the way of Crystal (Ruby version of Nim) and is just kind of there but mostly forgotten and doomed to be abandoned unless it finds a niche.
> What sets Zig apart is compile-time
I see this claim a lot, but I'm not sure. I think it's the fact that Zig is more or less still C syntax and semantics, just... fixed. The way it does comptime does seem better than how other languages do it, but I haven't actually used it, much less for long enough to judge it properly.
But at the same time, I'm pretty sure that smaller/simpler is going to mean less safe.
Curious if there are any D users here who try to stay within that intersection?
The bigger problem is guaranteeing concurrency safety, which you can only provide with nonblocking IO. Everybody loves blocks though, so that's far away.
That depends. It is really a Hard NO for GC?
Crystal is low level enough with a GC and people have written a complete OS with it. On my mobile now so I can't provide link. But it was featured on HN a while ago.
That's quite the oversimplification...
C# is not a systems-programming language: it's an application-programming language that is heavily dependent on the CLR runtime environment. While those research-kernels certainly do bring a-kind-of-CLR into the kernel it's far from being like the CLR in .NET; but they don't use C# - at least, not the same C# you use in Visual Studio: those research kernels: Singularity, Midori and Verve - used not only the Sing# and Spec# extensions to C#, they had their own compiler (Bartok) which itself enabled other language-extensions.
That said, those extensions are fascinating reads:
Sing# concerns message-passing ( https://www.microsoft.com/en-us/research/wp-content/uploads/... )
Spec# added Ada-style (i.e. compiler-enforced) invariants and pre/post-conditions (this was the basis for the Code Contracts feature which was annoyingly/tragically killed-off during the .NET Core reboot in 2016); see https://www.microsoft.com/en-us/research/project/spec/
Bartok: https://www.microsoft.com/en-us/research/wp-content/uploads/...
Incorrect, the main restriction are the targets supported by CoreCLR/NativeAOT/Mono(to an extent).
Or, at least, if you pick all memory-safe languages with GC, C# offers the most when it comes to low-level access.
I'm going to assume the author isn't trying to write a kernel.
There's also this paper, "Memory Safety in Ada, SPARK, and Rust"
https://www.adacore.com/papers/memory-safety-in-ada-spark-an...
https://blog.adacore.com/should-i-choose-ada-spark-or-rust-o...
Then you have FreePascal, as FOSS ObjectPascal dialect.
On Apple's turf, Swift naturally, given its bindings for all Metal anything frameworks.
https://github.com/nbrk/m2-raylib
Naturally given the way it has been largely ignored in the last 30 years, the choice is limited, yet GCC developers considered it had a community big enough to integrate GNU Modula-2 as standard frontend on GCC 14.
¹https://0xc0ffee.netlify.app/osdev
I, too, have been looking for a unicorn systems programming language, but it doesn't exit yet. Here's what I looked at so far (only languages that don't rely on a GC):
- C: lots of UB, less safe (memory- and type-wise)
- C++: too complex, not a big fan of OO
- Rust: too complex, gets in the way when writing mostly unsafe kernel code
- Zig: Good, but syntax is too noisy; lacks interfaces/dynamic dispatch
- Swift: Doesn't have a good bare metal story
- D: Seems interesting, I may be looking at it (although need to use betterC mode, i.e. not all language features)
- Odin: Game programming focused, small but growing community
- Ada: Strong candidate, but it has too much ceremony
- Pony: I like the capabilities model, but tiny community
- Hare: Also tiny community, lacks polymorphism
- Hylo (Val): Experimental (mutable value semantics), too immature
- Vale: Experimental (regional memory management), seems stalled
- V: Good candidate, but mixed reviews on its claims vs. reality
- Austral: Experimental (linear types), tiny community, not much progress
- Jakt: Built for Serenity OS, not sure if it's getting enough attention
- Jai: Focused on game programming, good reviews, but currently closed source (beta)
- Mojo: (Python-like) Seems very interesting and I'd give it a try, but too focused on GPU/parallel programming; also too early for adoption
On a side note and coming from high-level languages, D is the easiest to learn IMHO, as it is very dev-friendly, mature enough and has a good interoperability with C libraries.
I considered for a long time nim for its syntax and "low-levelness", but my lack of knowledge in low-level languages made the learning curve too much steep for me. I think I'll switch to it when I'll have more experience.
What intrigues me quite a bit relative to the main topic of the HN thread is Low[0]
> Low
is not only a language subset, but also a set of F* libraries that model a curated set of C features: the C memory model, stack- and heap-allocated arrays, machine integers, C string literals, and a few system-level functions from the C standard library. Writing in Low, the programmer enjoys the full power of F for proofs and specifications. At compile-time, proofs are erased, leaving only the low-level code to be compiled to C. In short: the code is low-level, but the verification is not.[0] https://fstarlang.github.io/lowstar/html/Introduction.html#t...
> Austral is a new systems programming language. It uses linear types to provide memory safety and capability-secure code, and is designed to be simple enough to be understood by a single person, with a focus on readability, maintainbility, and modularity.
Yes, this is a real project that really works. It can compile real and widely used software written in C including SQLite, OpenSSL, and CPython, giving them true memory safety without escape hatches.
From their manifesto [1]: "The biggest impediment to using Fil-C in production is speed. Fil-C is currently about 1.5x-4x slower than legacy C."
And for the moment being it is running only on Linux/X86_64, even though there should be nothing preventing it from running on other platforms.
[1]: https://github.com/pizlonator/llvm-project-deluge/blob/delug...
At most it should have pointer generics (similar to void* or Box<dyn Any>), where the generic function can't assume anything about the data, the data has to be heap-allocated, and the generic function can own and move the pointer around. Enough to implement collections, maybe iteration, and nothing else. Enough to not need monomorphization.
Oh maybe less preprocessing black magics :/
As for other stuff, I was working on a LLVM extension a while back to put a lot of Python-isms into C, like the array slicing, print statement, f strings, and decorators.
Generally though, I found that writing your own macros is actually easy, and you just have to type stuff out a bit more. Last project I worked on in C I had an include file that had all the macros and the defined functions to go with the macros. The print statement honestly takes up most of the file, cause its a combination of vararg in the macro with _Generic selector with functions defined on how to print each type of variable. But the rest of the stuff like array slicing is just a macro that calls a function that takes the slice arguments as a string. Doesn't look as nice as actual array slicing, but it works and easy to write.
I am not sure however I like the verbosity of it where if you are using 'raw' editors without snippet support, it becomes a chore very soon.
All in all it is nice to use and play around with.
How about Cyclone?
[0] https://ocaml.github.io/ocamlunix/index.html
[1] https://matt.might.net/articles/best-programming-languages/#...
For memory safety compared with Rust it does not use a borrow checker for the price of more checks and extra memory usage at runtime while avoiding GC or even reference counting.
I think this is very interesting trade off. Plus there are plans to implement few interesting ideas that even Rust type system cannot implement while keeping the language simple.
C# was kinda-sorta for this; as long as you're targeting an environment where GC is acceptable. (IE, as long as you're running in user-mode, as opposed to writing a kernel or a driver.)
It does allow for C-style pointers, which might be useful if you need to call a lower-level system API. (Unfortunately, you can't just suck in a header file.)
One thing you might miss is how errors, in Rust, are 0-cost. C# encourages exceptions over error codes, and throwing an exception has a much higher overhead than panic, because it captures a stack trace and allocates an object. (I must admit that I like Rust's error handling over C# because it differentiates cleanly "anticipated" errors versus exceptional situations.)
One project I’ve seen that I don’t think is particularly active but that I really like the ideas behind is borgo. It compiles to go (thus GC) but is decidedly more rustacean.
Check it out. I hope someone makes something like this wide spread.
PS. I have no affiliation with borgo, just an online fan.
ASP[0] was old way to do character animations for unix finger command .plan.
Recall HN post using python to do the same (handled animation independent of serial terminal speed).
Related HN post "John Carmack's .plan file" [3]
hascii mation [1]; Use standard graphics techniques and run through [2] or .plan with unscii[4]
"sheltered code" via mindcraft : https://youtu.be/7sNge0Ywz-M
------
ascii animation tutorial : https://www.youtube.com/watch?v=o5v-NS9o4yc
[0] :Ah, ASP link has been merged with /dev/null. :(
ASP and related things for animated .plan : https://superuser.com/questions/253308/scrolling-plan-file
[1] : https://github.com/octobanana/asciimation[2] : https://www.geeksforgeeks.org/converting-image-ascii-image-p...
[3] : "John Carmack's .plan file" : https://news.ycombinator.com/item?id=3367230\
[4] : unscii : http://viznut.fi/unscii/
I don't remember specifics, but there are some odd footguns to look out for.
and the related discussion https://lobste.rs/s/c3dbkh
Serious total MSLs that have a defined memory model to allow "low level" operations seem to be scarce if not any. Hence I do not think there is any single one that comes between C/Zig and Rust.
I would have said https://www.hylo-lang.org/ but, personal opinion, seems like there is too much going on as well. See also https://vale.dev/
P.S Mind mentioning what FP-like features are in Rust? (Genuinely have not looked into Rust that much but I am interested).
If you want the simplicity of C with more safety, maybe tooling like Frama-C, a MISRA C conformance checker, or just aggressive use of static and dynamic analysis tools like ASAN and UBSAN. You can also disable certain optimizations (e.g., strict aliasing) to steer away from some of the major pitfalls of UB.
Modula-2 (e.g. GCC).
Oberon (e.g. Oberon, Oberon 2, Oberon-07, Oberon+.)