NHacker Next
login
▲A Rust shaped holemnvr.in
112 points by vishnumohandas 2 days ago | 279 comments
Loading comments...
Expurple 22 hours ago [-]
> If someone posts a patch or submits a PR to a codebase written in C, it is easier to review than any other mainstream language. There is no spooky at a distance. [..] Changes are local.

Lol, wut? What about about race conditions, null pointers indirectly propagated into functions that don't expect null, aliased pointers indirectly propagated into `restrict` functions, and the other non-local UB causes? Sadly, C's explicit control flow isn't enough to actually enable local reasoning in the way that Rust (and some other functional languages) do.

I agree that Go is decent at this. But it's still not perfect, due to "downcast from interface{}", implicit nullability, and similar fragile runtime business.

I largely agree with the rest of the post! Although Rust enables better local reasoning, it definitely has more complexity and a steeper learning curve. I don't need its manual memory management most of the time, either.

Related post about a "higer-level Rust" with less memory management: https://without.boats/blog/notes-on-a-smaller-rust/

amluto 11 hours ago [-]
Even ignoring undefined behavior and nulls, everything in C is mutable. Action at a distance is basically the norm.
BiteCode_dev 10 hours ago [-]
And global states.

I remember the first time I was using gettext and wonder "wait, why do I have to switch the language for my whole program if I need it for just this request?" and realized that's because GNU gettext was made like that.

amluto 3 hours ago [-]
This even works across processes. The initial locale comes from environment variables, and you can’t generally rely on any particular values.

In fact, the “correct” values aren’t even portable. As far as I know, there is nothing resembling universal agreement on how to ask for what some systems call “C.UTF-8”. And, no offense to non-Americans, it’s fine [0] to spell one and one half “1,500”, but it is utterly broken to invoke this behavior by means of an environment variable that no one can quite agree on.

[0] OK, it really is ridiculous that the same string of Arabic numerals, commas and periods means different things in different places.

pjmlp 9 hours ago [-]
The C myth keeps being perpetuated, sadly.

And had not GNU/FSF made C the official main language for FOSS software on their manifesto, by the time when C++ was already the main userspace language across Windows, OS/2, Mac OS, BeOS, that "It is the reason for C's endurance" would be much less than it already is nowadays, where it is mostly UNIX/POSIX, embedded and some OS ABIs.

Xunjin 1 hours ago [-]
It's funny because that was the main argument against Rust when I introduced it at the company I worked for before, but the tool has been widely used internally ever since. Because made it easy to understand the locality PRs and how it solved most safety issues (they still need some unsafe usage).
whytevuhuni 11 hours ago [-]
If anything I'd like an even lower-level Rust.

There's so many good high-level languages to choose from, but when you need to go low-level, there's essentially only C, C++, Rust. Maybe Zig once it reaches 1.0.

What we need isn't Rust without the borrow checker. It's C with a borrow checker, and without all the usual footguns.

dxxvi 11 minutes ago [-]
> What we need isn't Rust without the borrow checker. It's C with a borrow checker, and without all the usual footguns

I agree with the other comment. Then what you need is Rust without all the bells and whistles (pattern matching, Cow, Rc, Result, Option ...).

sealeck 7 hours ago [-]
> What we need isn't Rust without the borrow checker. It's C with a borrow checker, and without all the usual footguns.

Which would look a lot like... Rust!

DeepSeaTortoise 39 minutes ago [-]
I disagree. I like playing around in Rust, but it is near impossible to reason about what instructions will end up being fed to your CPU without having the compiler emit the underlying assembly.

And that makes it very difficult to understand the performance of your code. Just a few days ago I couldn't wrestle the compiler into unrolling a loop (because of a get_unchecked that was hard to reason about), dropping performance of the entire data structure to as little as 30% of a quick proof of concept implementation. The asserts are powerful tools, but only if you can figure out why the compiler is not behaving nicely and how to construct the conditions that will convince it...

whytevuhuni 7 hours ago [-]
Rust is more closely related to C++ than C.

I want Rust, but without panics, without generics (aside for lifetimes), without traits and bounds (aside for lifetimes), without operator overloading, without methods on structs, without RAII, without iterators, etc.

borkido 7 hours ago [-]
What would the benefit be of not associating functions that operate on a struct with said struct? You would just end up with a bunch of functions that take a struct of that type as their first argument.
whytevuhuni 6 hours ago [-]
> You would just end up with a bunch of functions that take a struct of that type as their first argument.

I don't see that as a necessarily bad thing, it brings uniformity with the rest of the functions. I've coded with libraries like APR, Nginx's API, and GTK's GLib, and the result looks quite aesthetically pleasing too (subjective of course).

I'm also not considering this as a deal-breaker or anything. The point is not that this one particular feature makes a language complex, but rather that features like these tend to pile up, and what once looked like a simple language quickly becomes less simple due to dozens of such simple features added together. But one or two of these is fine.

tialaramex 5 hours ago [-]
Unlike C++ all Rust's methods already can be used this way

    '5'.is_digit(10) // Is literally the same as
    char::is_digit('5', 10) // this
Basically all a "method" is in Rust is an associated function where the first parameter has the magic keyword self instead of needing a real name and type.

Rust even has guidelines recommending you consider e.g. &self rather than having an associated function whose first argument has type &Self, but noting that you should not do this if your type has "smart pointer" behaviour where users expect to call methods on the inner value instead. For example Box and Arc provide associated functions but not methods.

sealeck 6 hours ago [-]
You can also just... not use any of these features. It won't be pleasant, but it's doable. And some projects _do_ do this!
throwawaymaths 2 hours ago [-]
except you would have macros, protocols, raii, a library system fragmented over async/noasync...
Expurple 10 hours ago [-]
> There's so many good high-level languages to choose from

We have many popular high-level languages, but I disagree that they are good. Most of them are fragile piles of crap unsuitable for writing anything larger than a throwaway script.

(In my subjective and biased assessment, which is hovewer based on professional experience)

delusional 9 hours ago [-]
That's an interesting opinion. Could you mention one that wasn't fragile? Just to get a sense of the shape of these classes.
Expurple 8 hours ago [-]
They are all fragile for different reasons: dynamic typing (that always leaks through bolted-on type hints anyway), implementation inheritance, null, exceptions, uncontrolled mutation, abuse of reflection and downcasts, weak leaky interfaces that the compiler doesn't check, no help with issues in multithreaded code, insecure defaults, poor semver practices in the ecosystem, poor tooling, fragile runtime dependencies.

I use Rust. From what I've read, Swift seems better than the others, with Typescript and Go following closely. All four happen to be widely praised in this thread.

To clarify, I don't have "professional experience" with every popular language, including Swift and Typescript too.

rahkiin 10 hours ago [-]
It would be an interesting excercise just to see how such a language would look.

Would modules be needed or can preprocessing work still. How more advanced will the type system need to be. And how will pointers chanhe to fix all footguns and allow static borrow checking.

lelanthran 10 hours ago [-]
> It would be an interesting excercise just to see how such a language would look.

I started designing one (C-with-borrow-checker) way back in 2018; never got around to finishing the design or making a MVP, but I believe you can solve maybe 90% of memory problems (use after free/double free, data racing, leaking, etc) with nothing more than some additional syntax[1] to type declarations and support for only fat arrays.

IIRC, I thought that the above (coupled with an escape hatch for self-referential and/or unsafe values) was all that was needed to prevent 90% of memory errors.

Somewhere along the way scope-creep (objects would be nice, can't leave out generics, operator overloads necessary for expressing vector math, etc) turned what would have been a small project into a very large one.

-------------------------------

[1] By additional syntax, I mean using `&` and `&&` in type declarations as a qualifier similar to how `static`, `const`, `volatile`, etc qualifiers are used.

buzzin__ 4 hours ago [-]
"C is the language that combines raw power of assembly with expressiveness of assembly."
trealira 1 hours ago [-]
> "C is the language that combines raw power of assembly with expressiveness of assembly."

The most expressive part of C is the syntax oriented around writing terse, side-effectful expressions that manipulate buffers, pointers and counters, with the array of precedences and the pre- and post-increments and assignment operators and short-circuiting operators. You can write stuff like this:

  while (--n > 0 && (c = getchar()) != EOF && (*s++ = c) != '\n')
      ;
  *s = '\0';
Or these snippets (taken from K&R):

  // Parsing flags for command line arguments
  while (--argc > 0 && (*++argv)[O] == '-')
      while (c = *++argv[O])
          switch (c) {
          // ...
          }

  // Last line of a buffered `getchar()` implementation 
  return (--n >= 0) ? (unsigned char) *bufp++ : EOF;
You can write other programs in C, like GUI programs and compilers, but it's not as nearly tailor made for such programs and it's basically just like assembly, like you said.
pjmlp 9 hours ago [-]
Modula-2, Object Pascal, Ada,....

Zig is basically Modula-2 in C syntax clothing.

atoav 6 hours ago [-]
I program both C/C++ and Rust (the latter to a lesser degree currently). Rust is far superior to C in Error locality, if you write ideomatic Rust. Most of the types of errors I make in C would have been cought at compile time in Rust.

Aside from Rusts ownership model you can use the type system to enforce certain things. A typical example is that Rust uses different String types to force programmers to deal with the pitfalls. Turns out if you have a file name in an operating system it could not be a valid string, or you could have valid Unicode text that could not be a filename. Rust having different types for OS Strings and internal Unicode means when you want to go from one to the other you need to explicitly deal with the errors or choose a strategy how to handle them.

Now you could totally implement strings within Rust in a way that wouldn't force that conversion and programmers would then yolo their way through any conversion, provided they even knew about the issue. And the resulting error would not necessarily surface where it orginated. But that would be programming Rust like C.

In my experience many C libraries will just happily gulp up any input of any remotly valid shape as if it was valid data without many devs being even aware there were cases or conversions they would have had to deal with. You recognize exceptionally good C devs by the way they avoid those pitfalls.

And these skilled C devs are like seasoned mountaineers, they watch their every step carefully. But that doesn't mean the steep north face of the mountain is the safest, fastest or most ergonomic way to get to the summit. And if you believe that C is that, you should be nowhere near that language.

8 hours ago [-]
shmerl 12 hours ago [-]
Yeah, and C can get unreadable fast. Obfuscated C contest is a great example: https://www.ioccc.org

But that doesn't mean it's a good idea to use such style for PRs, lol.

Expurple 11 hours ago [-]
That's a really weird example to give. You can write unreadable code in any language if you're determined enough
shmerl 11 hours ago [-]
Well, C has such contest, not every language on the other hand.
staunton 10 hours ago [-]
I'd guess that's mostly because C has been around long enough to be chosen for it and people who are into that kind of thing don't urgently need more than one contest, regardless of language...
peppingdore 11 hours ago [-]
Rust is unreadable by default
shmerl 11 hours ago [-]
Syntax could be less terse and abbreviated, I agree. But it's not unreadable. On the other hand some complain that Rust sometimes is too verbose, so I guess it's some balance.
throwawaymaths 2 hours ago [-]
verbosity is not really the same as nonreadability.

i would say the biggest factor for nonreadability is not being able to chase function calls using simple text search, with as few indirections as possible. proc macros and to a lesser degree traits make that difficult.

shmerl 1 hours ago [-]
Yeah. You can to use LSP to work around it for usage search (which is really helpful), but it's definitely not a human readable case if function name is obscured.
flohofwoe 8 hours ago [-]
> But it's not unreadable.

That's just because you got used to it ;) (same as with modern C++ really, if you've used C++ long enough you become blind to its problems)

sagacity 7 hours ago [-]
This is completely true.

"Regular" Rust and C++ is fairly readable, but a quick Google for "Higher Kinded Types in Rust" ends up with [0]:

  fn ap<A, B, F: Fn(&A) -> B>(x: &<Self as Plug<F>>::R, arg: &<Self as Plug<A>>::R) -> <Self as Plug<B>>::R
          where Self: Plug<A> + Plug<B> + Plug<F>;
[0] https://hugopeters.me/posts/14/
uecker 22 hours ago [-]
It is a bit unclear to me why somebody who rejects C++ because "I once spent an entire year in the heaven of C++, walking around in a glorious daze of std::vector and RAII, before one day snapping out of it and realizing that I was just spawning complexity that is unrelated to the problem at hand." (which I can absolutely agree with!) is picking Rust from all options. If there is a language that can rival C++ in terms of complexity, it is Rust.
tialaramex 21 hours ago [-]
I've seen this idea from a few people and I don't get it at all.

Rust is certainly not the simplest language you'll run into, but C++ is incredibly baroque, they're not really comparable on this axis.

One difference which is already important and I think will grow only more important over time is that Rust's Editions give it permission to go back and fix things, so it does - where in C++ it's like venturing into a hoarder's home when you trip over things which are abandoned in favour of a newer shinier alternative.

pjmlp 9 hours ago [-]
Because of the type system, with its ML influence, two macro systems, the stuff on nightly that many folks enjoy using, having to rely on external crates for proper error handling and async/await features.

Additionally, given its ML influence, too many people enjoy doing Haskell level FP programming in Rust, which puts off those not yet skilled in the FP arts.

Also the borrow checker is the Rust version of Haskell burrito blogs with monads, it is hard to get how to design with it in mind, and when one gets it, it isn't that easy to explain to others still trying to figure it out.

Hence why from the outside people get this opinion over Rust.

Naturally those of us with experience in compilers, type systems theory and such, see it differently, we are at another level of understanding.

Ygg2 8 hours ago [-]
> Also the borrow checker is the Rust version of Haskell burrito blogs with monads, it is hard to get how to design with it in mind,

Eh. Haskell monads are a math-centric way of performing operations on wrapped types (stuff inside a monad). Rust borrow checker is way more pragmatic-centric, i.e. how can we prevent certain behaviors.

The difference being you don't see Monads being replaced by Tree-Monads, without impacting the code.

> and when one gets it, it isn't that easy to explain to others still trying to figure it out.

So is going from 0-based to X-based arrays (where X is an integer); Or learning a new keyboard layout. Just because it's hard (unfamiliar) doesn't mean it's impossible.

pjmlp 8 hours ago [-]
In a way you're right, hence my last sentence.
rich_sasha 21 hours ago [-]
C++'s complexity is coming from how eager it is to let you shoot yourself in the foot. Rust will make you sweat blood to prove the bird in the sky you're shooting at is really not your own foot.
bcrl 15 hours ago [-]
And the gun isn't a gun, it's just a bunch of rope that you somehow got tangled into an explosive with a detonator you can't quite seem to locate.
WD-42 11 hours ago [-]
You sure it doesn’t come from, for example, needing a macro just to get the length of an array?
saghm 11 hours ago [-]
Yes, I think it's pretty easy to be sure that this isn't where the complexity comes from when there's literally a method to do that on arrays. I'm not even sure which language you're talking about here, because you can call `.len()` in Rust or `.size()` in C++.

If you're trying to remember what the language is where there's no immediately obvious straightforward way to get the length of an array, it's not Rust or C++; you must have been thinking of C.

tialaramex 10 hours ago [-]
This is a case of exactly the "hoarder" modality in C++ that I described. When somebody says array in C++ you think of std::array, the newer shiny C++ class which you're asked to use instead of the native array types. The native array types in C++ still exist and indeed do not provide methods like size()

In Rust the arrays aren't stunted left over primitive types which weren't gifted modern features, array.len() works because all Rust's types get features like this, not just the latest and greatest stuff.

eMSF 7 hours ago [-]
While it is true that only class types have member functions in C++, that does not mean that objects of other types demand the use of macros. C++ also supports non-member functions, and the standard library contains a fair amount of these; including `std::size` that can be used to "get" the length of an array.

(C++ arrays are different from arrays in many other programming languages, though not necessarily Rust, in that their type specifies their length, so in a way this is something you already "have" but certainly there are cases where it is convenient to "get" this information from an object.)

WD-42 4 hours ago [-]
Ah, std::size, since c++17. No wonder nobody knows about it, that’s after we all started switching to rust.
eldenring 17 hours ago [-]
It's really not that bad.
18 hours ago [-]
viccis 12 hours ago [-]
>where in C++ it's like venturing into a hoarder's home when you trip over things which are abandoned in favour of a newer shinier alternative

Perl is so bad about this that I once worked on a very old codebase in which I could tell approximately when it was written based on which features were being used.

adastra22 12 hours ago [-]
C++ has editions too btw. C++11, C++14, C++17, etc. These are opt in and allowed to break compatibility, although that is very rarely done in practice.
Expurple 11 hours ago [-]
> although that is very rarely done in practice.

That's one difference. And the other important differences are:

- Rust apps can depend on library "headers" written in other editions. That's the whole deal with editions! Breaking changes are local to your own code and don't fracture the ecosystem.

- Rust has a built-in tool that automatically migrates your code to the next edition while preserving its behavior. In C++, upgrading to the next standard is left as an exercise for the reader (just like everything else). And that's why it's done so rarely and so slowly.

pjmlp 9 hours ago [-]
Not really, because the crates they depend on cannot expose APIs with semantic changes across versions.

Also it requires everything to be compiled with the same compiler, from source code.

There are tools available in some C++ compilers for migration like clang, note the difference between ISO languages with multiple implementations, and one driven by its reference compiler.

Expurple 8 hours ago [-]
> Not really, because the crates they depend on cannot expose APIs with semantic changes across versions.

Not sure what you're talking about. Any specific examples?

> Also it requires everything to be compiled with the same compiler, from source code.

It's not related to editions at all. It's related to not having an implicit stable ABI.

It's possible have a dynamic Rust library that exposes a repr(C) interface, compile it into an .so using one version of the compiler, and then compile the dependent "pure Rust" crates using another compiler that's just going to read the metadata ("headers") of that library and link the final binary together. Same as in C and C++. You just can't compile any Rust code into a stable dynamic library, by defalut. (You can still always compile into a dylib that needs a specific compiler version)

pjmlp 8 hours ago [-]
To avoid repeating myself, here is an example from 4 years ago.

https://news.ycombinator.com/item?id=26966151

Expurple 7 hours ago [-]
As the other commenter responded there, your example isn't about editions at all. It's about mixing ABIs and mixing multiple versions of the language runtime. Those are entirely separate issues.

You're correct that the possible changes in editions are very limited. But editions don't hinder interoperability in any way. They are designed not to. Today, there are no interoperability problems caused by editions specifically.

> compromises will be required, specially regarding possible incompatible semantic differences across editions.

That's just an assumption in your head. 4 editions later, it still hasn't manifested in any way.

pjmlp 6 hours ago [-]
4 editions later, Rust is yet to be used at the scale of C and C++ across the industry, my point on the comment is how editions will look like after 50 years of Rust history.

ABIs and multiple versions of the language runtime, are part of what defines a language ecosystem, hence why editions don't really cover as much as people think they do.

Expurple 6 hours ago [-]
Editions will look like band-aids that don't fully solve the cruft accumulated over the 50 years. That's not hard to predict. It's still a very useful mechanism that slows down the accumulation of said cruft. I'm yet to see a language that has a better stability/evolution story
pjmlp 5 hours ago [-]
In any case, while I as language geek have these kind of discussions, given current progress in AI systems, my point of view is that we will get the next evolution in programming systems, thus it won't matter much if it is C, C++, Rust, C#, Java, Go or whatever.

We (as in the industry) will eventually get reliable ways to generate applications directly to machine code, just as optimizing compilers took a couple of decades to beat hand written Assembly, and generate reliable optimized code.

So editions, regardless of what they offer, might not be as relevant in such a timeframe from a couple of decades ahead.

Expurple 4 hours ago [-]
The machine code will always be generated from "something". A one-line informal prompt isn't enough. There will always be people who write specs. Even the current languages are already far from "machine code" and could be considered "specs", albeit low-level
adastra22 4 hours ago [-]
As the response to that comment points out, you are confusing editions for ABI changes. Different editions are purely a source-level change in the language. All editions are ABI-compatible.
tialaramex 11 hours ago [-]
C++ shipping new and slightly incompatible versions of the entire language every three years isn't Editions, there was a proposal to attempt Editions (under the name "Epochs") for C++ but it faced significant headwinds and was abandoned.
pjmlp 9 hours ago [-]
On the other hand, editions do less that what people think they actually do.
blub 9 hours ago [-]
Because they’re both complicated languages, but for different reasons. Rust didn’t really solve memory safety, it just pushed all the complexity into the type system. If one was struggling with memory errors in C++ that’s nice. If one was using Java, that still sucks.

Furthermore some programmers really like complicated languages like Rust, Haskell, etc others like straightforward languages like Go, Python, etc.

pjmlp 9 hours ago [-]
Go pushes the complexity into the programmer, that is how one ends with source code like Kubernetes, when the language doesn't provide all the tooling.

Python belongs to the complicated languages section, people that think Python is straightforward never bothered reading all the manuals, nor had to put up with all the breaking changes throughout its history, it wasn't only 2 => 3, that was the major event, every release breaks something, even if little.

ReaperCub 5 hours ago [-]
> Go pushes the complexity into the programmer, that is how one ends with source code like Kubernetes, when the language doesn't provide all the tooling.

I am not sure what you mean by this. I write go code pretty much everyday and that code looks vaguely the same as it would do in C#, or Python, or JavaScript.

> Python belongs to the complicated languages section, people that think Python is straightforward never bothered reading all the manuals, nor had to put up with all the breaking changes throughout its history, it wasn't only 2 => 3, that was the major event, every release breaks something, even if little.

I've read the manuals. Most docs appear to be reasonably well written, even if I find many of the examples a bit odd (I've never been a big fan of Monty Python, so the spam and eggs examples are all a bit odd).

The 2 => 3 change was probably a big thing for those migrating all the existing libraries, frameworks. But as someone that uses it for prototyping stuff, I barely noticed the difference.

Expurple 8 hours ago [-]
Go is "straightforward" at the cost of making your codebase not straightforward: https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-... . Programming is complex. That complexity has to live somewhere.

Python is absolutely not straighforward, it's a huge language with many moving parts and gotchas.

Although, I admit, both are very easy to start programming in and to start shipping broken projects that appear working at first. They are good for learning programming, but terrible for production.

> Rust didn’t really solve memory safety, it just pushed all the complexity into the type system.

Yes, that's what it did, and that's the right tradeoff in most cases. Where the compiler can't make a reasonable default choice, it should force the programmer to make a choice, and then sanity-check it.

> If one was struggling with memory errors in C++ that’s nice. If one was using Java, that still sucks.

It's nice for those struggling with uncaught exceptions, null pointer bugs, mutithreading bugs, and confusing stateful object graphs in Java.

blub 8 hours ago [-]
Many of us strongly prefer handling some complexity ourselves that we can then tackle with tests, CI, fuzzing, etc if it means we don’t have to jump through hoops to satisfy the compiler before we can even see our code running.

Yes, Golang and Python and Java are very easy to start programming in. And unless we’re dealing with some really complex problem, like next-gen cryptocurrencies ;), by the time the Rust teams have gotten their code working and nice and proper as any Rust code needs to be, the Golang/Python/Java teams have already released to customers.

If one wants to be super-cautios and move accordingly slower to be really really sure they have no memory errors then that’s fine. There’s a market for that stuff. But selling this approach as a general solution is disingenuous.

Expurple 7 hours ago [-]
> the Golang/Python/Java teams have already released to customers.

...have already released a broken prototype that appears to be working for now.

I'm yet to see a case where manually "hardening" your software is faster than writing a "similarly-good" program in Rust. That's just anti-automation. Why repeat the same work in every project and bloat your codebase, when the compiler can carry that burden for you? In my experience, Rust makes you write production-grade software faster than when using other languages.

> But selling this approach as a general solution is disingenuous.

I agree! There are legitimate cases where releasing a broken prototype as quickly as possible is important. There's room for that.

But I agrue that it's not the case for most "serious" production software that would be maintained for any period of time. And that Rust is the preferable option for writing such production software.

blub 3 hours ago [-]
The idea that one needs Rust to write reliable software is not only ridiculous at a logical level, it is also contradicted by the fact that Rust is but a tiny, irrelevant subset of safety-critical software or really all software. If it were really “preferable for writing such production software”, more people would be using it.

But they don’t because the compiler does not carry the burden for you. It puts the burden of writing code in a way that satisfies the Rust lifetime management design on you, and that’s what you’ll be doing forever.

There zero proof that Rust software is higher quality than equivalent e.g. Swift or Java. The memory-safety trick only works against C and C++.

Expurple 2 hours ago [-]
> The idea that one needs Rust to write reliable software is not only ridiculous at a logical level

I never said that. I said that it's more appropriate and productive.

> If it were really “preferable for writing such production software”, more people would be using it.

There are many valid reasons for not using it, even in new projects. Such as human preferences, lack of specific libraries, and simply not having time to learn yet another language for a non-10x benefit.

> the compiler does not carry the burden for you. It puts the burden of writing code in a way that satisfies the Rust lifetime management design on you, and that’s what you’ll be doing forever.

It carries a different burden for me. In exchange for the burden that you mention, I don't worry about mutable aliasing bugs (including data races), breaking the code by refactoring it, manually unit-testing basic properties that could be types, debugging non-obvious non-local effects (such as exceptions and null values invisibly propagating through the layers of the app), micro-optimizing the performance manually, and so on. The benefits are known. I've almost forgot how to use a debugger at this point.

It's a tradeoff. I find it more comfortable and less cognitively-taxing to work this way, once the initial learning curve is behind. Less unpredictable problems to distract you from solving the problem. The borrow checker problems largely go away as you get more comfortable with it. And it's a skill that's instantly transferrable between all Rust projects, unlike project-specific defensive practices in other languages (the "manual hardening" needed to get decent quality). It's a long-term investment that automates a small, but not insignificant part of your work.

> There zero proof that Rust software is higher quality than equivalent e.g. Swift or Java.

The Google's director of engineering gave a talk saying that their Rust teams are just as productive as their Go teams, while also enjoying "a decreased defect rate over time": https://www.reddit.com/r/rust/comments/1bpwmud/media_lars_be...

> The memory-safety trick only works against C and C++.

That's absolutely true, and that's an important thing to keep in mind when framing discussions about Rust.

imtringued 7 hours ago [-]
Yeah, the thing is, the moment your software has crossed the boundary of success, it becomes a burden, not a blessing. You are now stuck with it and you better have made sure that you're happy with it the way you built it, because you're not moving on from this project any time soon.

Maybe Rust isn't optimized for throwaway projects and that's fine.

ViewTrick1002 6 hours ago [-]
I think you can do throwaway projects and prototypes about as fast in rust.

But it requires a different mindset which I think many in our industry finds hard to work with. Rust exposes the imperfections rather than hiding them until you explicitly check for them like in most other languages.

Just clone and unwrap liberally and throwaway projects go fast.

blub 3 hours ago [-]
What we’re seeing in practice is not throwaway software but successful companies built over many years using e.g. Golang. When they grow big enough to pay the Rust tax and can’t squeeze more performance out of their set-up they switch to Rust.

Starting something in Rust only makes sense for very few domains and software categories.

gooooood 2 hours ago [-]
I wonder how Go would fare if it had a production-ready LLVM/GCC backend. I wouldn't be surprised if much of the performance differences between Go and C/C++/Rust comes down to optimized codegen (rather than GC, which is what people often complain about Go). Not saying that GC pauses might not be an issue in some cases, but still...
imtringued 7 hours ago [-]
As someone who has to work with a janky mix of C++ and Python. I would prefer getting rid of both altogether. I personally am not a fan of Python or the Python ecosystem whatsoever. It's certainly not speeding things up with those two.
bryanlarsen 17 hours ago [-]
Reading between the lines, the author is a Haskell fan. Haskell is another "complicated" language, but the complexity feels much different than the C++ complexity. Perhaps I would describe it as "complexity that improves expressiveness". If you like Haskell for its expressiveness but dislike C++ for it's complexity, I suspect Rust is a language you're going to like.
fn-mote 11 hours ago [-]
My impression was the opposite. I wondered how well the author knew Haskell. They mention:

(1) The "intimidating syntax". Hey, you do not even need to be using <$> never mind the rest of those operators. Perl and Haskell can be baroque, but stay away from that part of the language until it is useful.

(2) "Changes are not localized". I'm not sure what this means. Haskell's use of functions is very similar to other languages. I would instead suggest referring to the difficulty of predicting the (time|space) complexity due to the default lazy evaluation.

FTA:

> In contrast, Haskell is not a simple language. The non-simplicity is at play both in the language itself, as evidenced by its intimidating syntax, but also in the source code artifacts written in it. Changes are not localized, the entire Haskell program is one whole — a giant equation that will spit out the answer you want, unlike a C program which is asked to plod there step by step.

Edited to make the critique more objective.

dijit 22 hours ago [-]
You’re right that Rust is a ball of complication.

I am a fan of Rust but it’s definitely a terse language.

However there are definitely signs that they have thought about making it as readable as possible (by omitting implicit things unless they’re overwritten, like lifetimes).

I’m reminded also about a passage in a programming book I once read about “the right level of abstraction”. The best level of abstraction is the one that cuts to the meat of your problem the quickest - spending a significant amount of time rebuilding the same abstractions over and over (which, is unfortunately often the case in C/C++) is not actually more simple, even if the language specifications themselves are simpler.

C codebases in particular, to me, are nearly inscrutable unless I spend a good amount of time unpicking the layers of abstractions that people need to write to make something functional.

I still agree that Rust is a complex language, but I think that largely just means it’s frontloading. a lot of the understanding about certain abstractions.

uecker 22 hours ago [-]
I do not really agree with respect to C. I often have to deal with C code written by unexperienced programmers. It is always relatively easy to refactor it step by step. For C++ this is much more painful because all the tools invented to make the code look concise make it extremely hard to follow and change. From the feature set, I would say that Rust is the same (but I have no experience refactoring Rust code).
bryanlarsen 17 hours ago [-]
> but I have no experience refactoring Rust code

But you're willing to write many comments complaining that Rust is hard to refactor. Rust is the easiest language to refactor with I've ever worked in, and I've used a couple dozen or so. When you want to change something, you change it, and then fix compiler errors until it stops complaining. Then you run it, and it works the first time you run it. It's an incredible experience to worry so little about unknown side-effects.

adastra22 11 hours ago [-]
Also, although it was not designed with this in mind, the described process adopts VERY well to LLMs. You can make a refactoring change, then tell the LLM to "run cargo check and fix the errors." And it does a very good job of doing this.
bryanlarsen 2 hours ago [-]
An LLM will do it with fewer clicks, but the rust-analyzer LSP hook on your editor also helps tremendously.

Both the LLM and humans doing it are relying on the fabulous work the rust compiler team has done in generating error messages with clear instructions on how to fix problems.

Dylan16807 16 hours ago [-]
> But you're willing to write many comments complaining that Rust is hard to refactor.

Their refactoring comments look focused on C versus C++ to me, with a bit of guessing Rust is like C++ in a way that is clearly labeled as speculation.

So I don't see the problem with anything they said about refactoring.

uecker 11 hours ago [-]
No, I wrote that Rust is too complex. And I responded to a comment that claims that C is hard to refactor, and this does not match my experience.
bsder 15 hours ago [-]
> Rust is the easiest language to refactor with I've ever worked in

Oh? So how do you refactor a closure into a named function in Rust?

I have found this to be of the most common failure modes that makes people want to punch the monitor.

(Context: in almost all programming languages, a closure is practically equivalent to an unnamed function--refactoring a closure to a named function tends to be pretty straightforward. This isn't true for Rust--closures pick up variable lifetime information that can be excruciatingly difficult to unwind to a named function.)

bryanlarsen 15 hours ago [-]
Set the type of all the parameters to your new function to bool, then compile. The compiler error will tell you, "Error: you passed a foo<'bar, baz> instead of a bool". Then you change the type in the function's parameter to "foo<'bar, baz>".
afdbcreid 12 hours ago [-]
You invoke the assist of rust-analyzer that does that automatically.
AstralStorm 21 hours ago [-]
In personal experience, the much stronger and composite type model in Rust makes it easier to refactor.

Adding features in particular is a breeze and automatically the compiler/language will track for you the places that use only old set of traits.

Tooling is still newer though and needs polish. Generic handling is interesting at times and there are related missing features for that in the language, vis a vis specializations in particular.

Basic concurrency handling is also quite different in Rust than other languages, but thus usually safer.

uecker 21 hours ago [-]
I am wondering about this. In C the nice thing is that you usually change things locally. A line of code does not depend on a million other things. I can't quite see how this works in Rust... At least in C++, template, overloading, etc. can make a single line depend on a lot of things.
rstuart4133 19 hours ago [-]
> I can't quite see how this works in Rust...

You won't understand it unless refactor some Rust programs.

Bunny summed it up rather well. He said in most languages, when pull on some thread, you end disappearing into a knot and you're changes are just creating a bigger knot. In Rust, when pull on a thread, the language tells you where it leads. Creating a bigger knot generally leads to compile errors.

Actually he didn't say that, but I can't find the quote. I hope it was something like that. Nonetheless he was 100% spot on. That complexity you bemoan about the language is certainly there - but it's different to what you have experienced before.

In most languages, complex features tend lead to complex code. That's what made me give up on Python in the end. When you start learning Python, it seems a delightfully simple yet powerful language. But then you discover metaclasses, monkey patching, and decorators, which all seem like powerful and useful tools, and you use them to do cool things. I twisted Python's syntax into grammar productions, so you could write normal looking python code that got turned into an LR(1) parser for example. Then you discover other peoples code that uses those features to produce some other cute syntax, and it has a bug, and when you look closely your brain explodes.

As you say C doesn't have that problem, because it's such a simple language. C++ does have that problem, because it's a very complex language. I'm guessing you are making deduction from those two examples that complex languages lead to hard to understand code. But Rust is the counter example. Rust's complexity is forces you to write simple code. Turns out it's the complexity of the code that matters, not the complexity of the language.

uecker 11 hours ago [-]
I only have limited experience with Rust (only playing around a bit). But it seems the language forces you to structure the code in a specific way (and you seem to agree). This is good, because it prevents you from making a mess (at some level at least). But what others report (and it matches my limited experience) is that it makes the structure of the code very rigid. So I do not quite see how this does not limit refactoring? What some of you describe in this thread is type-directed refactoring (i.e. you change a type and the compiler tells what you need to change), but is this not limited to relatively basic changes?

In C, you can make partial changes and accept a temporary inconsistency. This gives you a lot of flexibility that I find helpful.

Expurple 10 hours ago [-]
I'll put it like this. Rust causes you to refactor more often and do a full refactoring at once (because the interfaces are so rigid), but it makes it easy to do a full (and correct) refactoring very quickly. Think of it as docs being part of the interface and the compiler forcing you to always update the docs. That's an amazing property
Expurple 20 hours ago [-]
> A line of code does not depend on a million other things.

But it does! To qoute my top-level comment:

> What about about race conditions, null pointers indirectly propagated into functions that don't expect null, aliased pointers indirectly propagated into `restrict` functions, and the other non-local UB causes?

In other words: you set some pointer to NULL, this is OK in that part of your program, but then the value travels across layers, you've skipped a NULL check somewhere in one of those layers, NULL crosses that boundary and causes UB in a function that doesn't expect NULL. And then that UB itself also manifests in weird non-local effects!

Rust fixes this by making nullability (and many other things, such as thread-safety) an explicit type property that's visible and force-checked on every layer.

Although, I agree that things like macros and trait resolution ("overloading") can be sometimes hard to reason about. But this is offset by the fact that they are still deterministic and knowable (albeit complex)

uecker 20 hours ago [-]
This is true to some degree, but not really that much in practice. When refactoring you just add assertions for NULL (and the effect of derefencing a NULL in practice is a trap - that it UB in the spec is completely irrelevant. in fact it helps because it allows compilers to turn it a trap without requiring it on weak platforms). Restrict is certainly dangerous, but also rarely used and a clear warning sign, compare it to "unsafe". The practical issues in C are bounds checking and use-after-free. Bounds checking I usually refactor into safe code quickly. Use-after-free are the one area where Rust has a clear advantage.
Expurple 20 hours ago [-]
I agree that in practice NULL checking is one of the easier problems. I used it because it's the most obvious and easy to understand. I can't claim which kinds of unsoundness in C code are more common and problematic in practice. But that's not even interesting, given that safe Rust (and other safe languages) solve every kind of unsoundness.

> in fact it helps because it allows compilers to turn it a trap without requiring it on weak platforms

The "shared xor mutable" rule in Rust also helps the compiler a lot. It basically allows it to automatically insert `restrict` everywhere. The resulting IR is easier to auto-vectorize and you don't need to micro-optimize so often (although, sometimes you do when it comes to eliminating bound checks or stack copies)

> Restrict is certainly dangerous, but also rarely used and a clear warning sign, compare it to "unsafe".

It's NOT a clear warning sign, compared to `unsafe`. To call an unsafe function, the caller needs to explicitly enter an `unsafe` block. But calling a `restrict` function looks just like any normal function call. It's easy to miss an a code review or when upgrading the library that provides the function. That the problem with C and C++, really. The `unsafe` distinction is too useful to omit.

uecker 19 hours ago [-]
You are not wrong, but also not really right. In practice, I never had a problem with "restrict". And this is my point about Rust being overengineered. While it solves real problems, you need to exaggerate the practical problems in C to justify its complexity.
Expurple 19 hours ago [-]
> In practice, I never had a problem with "restrict".

That's exactly because it's too dangerous and the developers quickly learn to avoid it instead of using it where appropriate! Same with multithreading. C leaves a lot of optimization on the table by making the available tools too dangerous and forcing people to avoid them altogether.

That's how you get stuff like memory-safe Rust PNG decoders being 1.5x faster than established C alternatives that had much more effort put into them (https://www.reddit.com/r/rust/comments/1ha7uyi/memorysafe_pn...). Or the first parallel CSS engine being written in Rust after numerous failed attempts in C++ (https://www.reddit.com/r/rust/comments/7dczj9/can_stylo_be_i...). Read those threads in full, there are some good explanations there.

> you need to exaggerate the practical problems in C

I thought, the famous "70% of vulnerabilities" report settled this once and for all.

uecker 11 hours ago [-]
Na, sorry. The "70%" is just nonsense. And cherry picking individual benchmarks too.
Expurple 11 hours ago [-]
> The "70%" is just nonsense.

Care to elaborate why?

> And cherry picking individual benchmarks too.

Do you have any general, comprehensive benchmarks or statistics that would indicate the opposite? I would include one if I had one at hand, because that would be a stronger argument! But I'm not aware of such benchmarks. I have to cherry pick individual projects. I don't want to.

I still claim that, as a general trend, Rust replacements are faster while also being less bug-prone and taking much less time to write. Another such example is ripgrep.

antonvs 11 hours ago [-]
You're focusing purely on the problem-fixing aspects, but Rust's expressiveness is why I like it.

C can't match that. In C, you're basically acting as a human compiler, writing lots of code that could be generated if you used a more expressive language. Plus, as has been mentioned, it supports refactoring easily and safely better than any language outside of the Haskell/ML space.

The advantages of Rust are a package which includes safety, expressiveness, refactoring support. You don't need to exaggerate anything for that package to make sense.

uecker 11 hours ago [-]
I am not criticizing anyone for preferring Rust. I reject the idea that its complexity needs to imposed on everybody because of "safety"
antonvs 9 hours ago [-]
The "impose on everybody" is based on clear, objective metrics at the national and international level.

The fact that you, as an individual, prefer to use an unsafe, weakly typed language isn't very relevant to that.

The issue is not that it's not possible to write secure programs in C, the issue is that in practice, on average, people don't.

Pushing the use of memory-safe languages will reduce the number of security vulnerabilities in the entire software ecosystem.

anon-3988 11 hours ago [-]
> This is true to some degree, but not really that much in practice. When refactoring you just add assertions for NULL (and the effect of derefencing a NULL in practice is a trap - that it UB in the spec is completely irrelevant.

This happens a lot in discussion about programming complexity. What you are doing is changing the original problem to a much simpler one.

Consider a parsing function parse(string) -> Option<Object>

This is the original problem, "Write a parsing function that may or may not return Object"

What a lot of people do is they sidetrack this problem and solve a much "simpler problem". They instead write parse(string) -> Object

Which "appears" to be simpler but when you probe further, they handwave the "Option" part to just, "well it just crashes and die".

This is the same problem with exceptions, a function "appears" to be simple: parse(string) -> Object but you don't see the myriads of exceptions that will get thrown by the function.

uecker 11 hours ago [-]
I guess it depends on what the problem is. If the problem is being productive and being able to program without pain "changing the original problem to a much simpler one" is a very good thing. And "crash and die" can be a completely acceptable way to deal with it. Rust's "let it panic" is not at all different in this respect.

But in the end, you can write Option just fine it C. I agree though that C sometimes can not express things perfectly in the type system. But I do not agree that this is crucial for solving these problems. And then, also Rust can not express everything in the type system. (And finally, there are things C can express but Rust can't).

Expurple 10 hours ago [-]
> in the end, you can write Option just fine it C.

No, you can't. In the sense that the compiler doesn't have exhaustiveness checks and can't stop you from accessing the wrong variant of a union. An Option in C would be the same as manually written documentation that doesn't guarantee anything.

std::optional in C++ is the same too. Used operator* or operator-> on a null value? Too bad, instant UB for you. It's laughably bad, given that C++ has tools to express tagged unions in a more reliable way.

> And then, also Rust can not express everything in the type system. (And finally, there are things C can express but Rust can't).

That's true, but nobody claims otherwise. It's just that, in practice, checked tagged unions are a single simple feature that allows you to express most things that you care about. There's no excuse for not having those in a modern language.

And part of the problem is that tagged unions are very hard to retrofit into legacy languages that have null, exceptions, uninitialized memory, and so on. And wouldn't provide the full benefit even if they could be retrofitted without changing these things.

anon-3988 10 hours ago [-]
Yes, all I am saying is that we have to be honest what level of problems we are solving when we encounter a complicated solution.

The solution have to scale linearly with the problem at hand, that is what it means to have a good solution.

I agree with the article that Rust is overkill for most use cases. For most projects, just use a GC and be done with it.

> But I do not agree that this is crucial for solving these problems. And then, also Rust can not express everything in the type system.

This can be taken as a feature. For example, is there a good reason this is representable?

struct S s = 10;

I LOVE the fact that Rust does not let me get away with half-ass things. Of course, this is just a preference. Half of my coding time is writing one-off Python scripts for analysis and data processing, I would not want to write those in Rust.

> But in the end, you can write Option just fine it C.

Even this question have a deeper question underneath it. What do you mean by "just fine"? Because to me, tagged enums or NULL is NOT the same thing as algebraic data types.

This is like saying floating points are just fine for me for integer calculations. Maybe, maybe for you its fine to use floating points to calculate pointers, but for others it is not.

nemothekid 17 hours ago [-]
>When refactoring you just add assertions for NULL

This is a line of thinking I used to see commonly when dynamic typing was all the rage. I think the difference comes from people who view primarily work on projects where they are the sole engineer vs ones where they work n+1 other engineers.

"just add assertions" only works if you can also sit on the shoulder of everyone else who is touching the code, otherwise all it takes is for someone to come back from vacation, missing the refactor, to push some code that causes a NULL pointer dereference in an esoteric branch in a month. I'd rather the compiler just catch it.

Furthermore, expressive type systems are about communcation. The contracts between functions. Your CPU doesn't case about types - types are for humans. IMO you have simply moved the complexity from the language into my brain.

uecker 11 hours ago [-]
This was about refactoring a code base where a type is assumed to be non-null but it is not obvious. You can express also in C on interfaces that a pointer is non-null.
staunton 10 hours ago [-]
> interfaces

You mean obsolete and subtly wrong comments buried among preprocessor directives in some far-upstream header files?

pjmlp 9 hours ago [-]
Preprocessor enters the scene.

Then we have the functions that might be re-entrant or not, in the presence of signals, threads,...

sigi64 8 hours ago [-]
I can compare programming and refactoring large code bases in C and C++, Rust, and Python, from system and parsing and protocol libraries to asynchronous multi-threaded servers in the mentioned languages.

Refactoring Rust projects is clearly the easiest because the compiler and type system ensure the program is correct at least in terms of memory access and shared resource access. It doesn't protect me from memory leaks and logical errors. But since Rust has a built-in testing framework, it's quite easy to prepare tests for logical errors before refactoring.

C/C++ refactoring is a nightmare - especially in older projects without modern smart pointers. Every change in ownership or object lifetime is a potential disaster. In multi-threaded applications it's even worse - race conditions and use-after-free bugs only manifest at runtime, often only in production. You have to rely on external tools like Valgrind or AddressSanitizer.

Python has the opposite problem - too much flexibility. You can refactor an entire class, run tests, everything passes, but then in production you discover that some code was dynamically accessing an attribute you renamed. Type hints help, but they're not enforced at runtime.

Rust forces you to solve all these problems at compile time. When you change a lifetime or ownership, the compiler tells you exactly where you need to fix it. This is especially noticeable in async code - in C++ you can easily create a dangling reference to a stack variable that an async function uses. In Rust, it simply won't compile.

The only thing where Rust lags a bit is compilation speed during large refactors. But that's a small price to pay for the certainty that your code is memory-safe.

Another area where Rust absolutely excels is when using AI agents like Claude Code. It seems to me that LLMs can work excellently with Rust programs, and thanks to the support of the type system and compiler, you can get to functional code quickly. For example, Claude Code can analyze Rust programs very well and generate documentation and tests.

I think Rust with an AI agent has the following advantages:

Explicit contract - the type system enforces clear function interfaces. The AI agent knows exactly what a function expects and what it returns.

Compiler as collaborator - when AI generates code with an error, it gets a specific error message with the exact location and often a suggested solution. This creates an efficient feedback loop.

Ownership is explicit - AI doesn't have to guess who owns data and how long it lives. In C++ you often need to know project conventions ("here we return a raw pointer, but the caller must not deallocate it").

Fewer implicit assumptions - in Python, AI can generate code that works for specific input but fails on another type. Rust catches these cases at compile time.

anon-3988 11 hours ago [-]
> I do not really agree with respect to C. I often have to deal with C code written by unexperienced programmers. It is always relatively easy to refactor it step by step.

Really? How confident are you to change a data structure that uses an array with linear search lookup to a dictionary? Or a pointer that now is nullable (or is now never null)?

Unless you have rigorous test or the code is something trivial, this would be a project of its own.

I am pretty sure I can swap out the implementation of the dictionary in the rust compiler and by the time the compilation issues are worked out, the code would be correct by the end of it (even before running the tests)

tines 22 hours ago [-]
The two are incomparable in both quality and quantity. The complexity of Rust comes from the fact that it's solving complex problems. The complexity of C++ comes from a poorly thought out design and backwards-compatibility. (Not to slight the standards committee; they are smart people, and did the best with what they had.)

Anothere way of putting it is, if you didn't care about backwards-compatibility, you could greatly simplify C++ without losing anything. You can't say the same about Rust; the complexity of Rust is high-entropy, C++'s is low-entropy.

materielle 14 hours ago [-]
The C++ standard committee is definitely smart. But language design requires sense beyond just being smart.

They didn’t do the best with what they had. Sure, some problems were caused by C backwards compatibility.

But so much of the complexity and silliness of the language was invented by the committee themselves.

pjmlp 9 hours ago [-]
As someone that really enjoys C++, I would say that the current issues are cause by lack of preview implementations before being voted into the standard, this is just broken, but there are not enough people around to be able to turn the ship around.

Those folks eventually move to something else and adopt "C++ the good parts" instead.

uecker 22 hours ago [-]
I would say that most of the complexity in both cases comes from overengineering.
tines 22 hours ago [-]
Can you give some examples of Rust and C++ design that are over engineered?
uecker 21 hours ago [-]
For C++, just take lambda captures, you can do [&], [=], [a], [&a], [a = b] (and I probably forgot half), the many way you can do initialization in C++ became a meme, but it really permeates the whole language whose design is driven by enthusiasts and people who have an interest to make it more complex (trainers, book authors, consultants, etc.)

For Rust, I think it is a bit of a different story and it is harder to point to specific features. The language is clearly much better designed (because it was more designed and did not evolve so much) and because of its roots in functional programming. Just overall, the complexity is too high in my opinion and it a bit too idealistic and not pragmatic enough.

bfrog 14 hours ago [-]
The language is highly productive, it’s pragmatic enough. There’s no perfect language until our brains can directly describe what we want a machine to do without intermediary translation layers like C.
saagarjha 9 hours ago [-]
I think captures are mostly reasonable and fine (and other languages have similar constructs). Initialization is completely broken, yes.
LoganDark 14 hours ago [-]
> it a bit too idealistic and not pragmatic enough

This is an ideal programming language for certain types of people. It also gives the programming language certain properties that make it useful when provable correctness is a concern (see Ferrocene).

AlotOfReading 13 hours ago [-]
Last time I talked to the ferrocene people they had no plans to do anything with formal methods. Has their scope expanded?
_flux 7 hours ago [-]
Maybe it was intended to refer to RustBelt.

I haven't been following their work, though. It seems they are working on stacked borrows.

steveklabnik 26 minutes ago [-]
> I haven't been following their work, though. It seems they are working on stacked borrows.

We're a few years past that, there's now 'tree borrows' which just had its paper published: https://www.ralfj.de/blog/2025/07/07/tree-borrows-paper.html

LoganDark 5 hours ago [-]
Sorry, I didn't mean provable correctness as in using formal methods, I meant it in terms of far stronger compiler guarantees about especially things such as memory safety. I also personally find Rust code far more pleasant to write and to reason about compared to C/C++ because of how well-defined and consistent it is.
anon-3988 11 hours ago [-]
For C++ specifically, constructors. Just have functions for gods sake.
tines 4 hours ago [-]
The question was mainly meant to elicit examples for Rust (I asked about C++ just to get a baseline of what the original commenter thinks "overengineered" means), but nobody seems to have any for Rust :)
jcranmer 12 hours ago [-]
Having developed a fair amount of expertise with both C++ and Rust, C++ is on a completely different level of complexity from Rust.

In Rust, for most users, the main source of complexity is struggling with the borrow checker, especially because you're likely to go through a phase where you're yelling at the borrow checker for complaining that your code violates lifetime rules when it clearly doesn't (only to work it out yourself and realize that, in fact, the compiler was right and you were wrong) [1]. Beyond this, the main issues I run into are Rust's auto-Deref seeming to kick in somewhat at random making me unsure of where I need to be explicit (but at least the error messages basically always tell you what the right answer is when you get it wrong) and to a much lesser degree issues around getting dyn traits working correctly.

By contrast C++ has just so much weird stuff. There's three or four subtly different kinds of initialization going on, and three or four subtly different kinds of type inference going on. You get things like `friend X;` and `friend class X;` having different meanings. Move semantics via rvalue references are clearly bolted on after the fact, and it's somewhat hard to reason about the right things to do. It has things like most-vexing parse. Understanding C++ better doesn't give you more confidence that things are correct; it gives you more trepidation as you know better how things can go awry.

[1] And the commonality of people going through this phase makes me skeptical of people who argue that you don't need the compiler bonking you on the head because the rules are easy to follow.

6 hours ago [-]
tialaramex 10 hours ago [-]
The C++ 11 move semantic is definitely an example of C++ programmers being sold a "pig in a poke"† The claim in the proposal document was that although this isn't the "destructive move" which programmers wanted (and which Rust had by 2015), the C++ 11 move feature can be realised with less disruption and is just as good. In reality it left significant performance on the table and you can't get it back without significant further language change.

† This phrase would have been idiomatic many years ago but it is still used with the same intent today even though its meaning is no longer obvious, the idea is that a farmer at market told you this sack you can't see inside ("poke") has a piglet in it, so you purchase the item for a good price, but it turns out there was only a kitten in the bag, which (compared to the piglet) is worthless.

blub 8 hours ago [-]
Copy by default is the right way to go. Even if less performant, it’s safe and super-easy to understand. Let the people that want to squeeze the last drop of performance worry about moves…

Move by default is the thing which complicates Rust so much.

Expurple 8 hours ago [-]
Copy by default wouldn't be so bad if the move was destructive
wisty 17 hours ago [-]
I'm not at all fluent at Rust, but I think c++ is not just complex, but every c++ project is complete in a different way.
Aeolun 13 hours ago [-]
Rust is a lot better at producing helpful error messages than any C++ I’ve seen.
andrepd 16 hours ago [-]
It's really not even remotely the same. C++ has literally >50 pages of specification on the topic of initialising values. All of these are inconsistent, not subject to any overarching or unifying rule, and you have to keep it all in mind to not run into bugs or problematic performance.

Rust is a dead simple language in comparison.

blub 8 hours ago [-]
As the definition says, the C++ standard is a formal technical document.

Rust doesn’t have a standard, it has a book, so you should refer to the initialization section from Stroustrup’s C++ book to keep things fair.

Expurple 8 hours ago [-]
Rust doesn't need a formal document so badly, because (in safe code) the punishment for not following the rules is much more clear and small. It's either a logic bug that doesn't lead to memory-related vulnerabilities, or a readable compiler error. Of course, if your compiler doesn't catch and explain your errors to you, you need a separate book to help you with that!
tines 4 hours ago [-]
To be fair, the formal document isn't for language users, it's for compiler implementors. Rust doesn't have a formal document for other reasons, mainly that there is one main Rust compiler, as opposed to C or C++ where there are dozens that all need to agree on behavior.
steveklabnik 23 minutes ago [-]
People over-index on "formal" here, the Rust reference and Ferrocene (which will end up being adopted as the official spec) is just as "formal" as the C++ specification.

There are other compilers in development, and they're able to coordinate with these documents. There is of course always more work to do, but it's really not as far away as some people believe.

Expurple 4 hours ago [-]
Yeah, the historical reason for standardizing C is the reason to "reconcile" a bunch of already existing compiler implementations
blu3h4t 5 hours ago [-]
Why would everyone call rust a c+++ whet it is obviously an ocaml erlang hybrid :D
Narew 12 hours ago [-]
It's funny because I have completely the opposite stance. When I code in rust (mainly algorithm), I always struggle to change what I want to do to what rust allow me to do. And all this complexity has nothing to do with the problem.
jplusequalt 22 hours ago [-]
>If there is a language that can rival C++ in terms of complexity

Fair, but this relative. C++ has 50 years of baggage it needs to support--and IMO the real complexity of C++ isn't the language, it's the ecosystem around it.

dlachausse 22 hours ago [-]
Swift is a great C++ and Rust alternative that doesn’t get enough attention outside of Apple platforms. It’s a performant, statically typed, compiled language that feels almost like a scripting language to write code in. It’s memory safe, cross platform, has a fantastic standard library, and has excellent concurrency capabilities. Even the non-Xcode tooling is maturing rapidly.

The big weak spot really is lack of community outside of Apple platforms.

cosmic_cheese 20 hours ago [-]
I would love to see a cross-platform desktop UI toolkit for Swift, preferably one that’s reactive and imperative-dominant with declaritivity sprinkled in where it makes sense (all-in declarative design like SwiftUI hits too many language weak points for the time being). Swift is really quite nice to write once you get a feel for it, and as long as one is judicious about advanced feature use, it looks more familiar and less intimidating than Rust does which is great for newcomers.
willtemperley 23 minutes ago [-]
Swift is coming to Android. That may improve traction. [1]

Having just ported a small library from Rust to Swift, it’s fairly clear that Rust is ridiculously fast but Swift is just so much more readable. Much easier to debug too.

Also - why would Rust only have a max-heap? In the library I ported, the authors had to reverse the algorithmic logic in the entire library for this.

Even though it’s only about half as fast as Rust, Swift performance does start to shine with concurrency.

[1] https://www.swift.org/android-workgroup/

shim__ 8 hours ago [-]
Swift seems more like an Go or Python alternative
rapsey 10 hours ago [-]
Swift has IMO surpassed Rust in complexity. Rust changes very little but Swift has gone through multiple major changes and is accumulating cruft.
nlitened 5 hours ago [-]
> To paraphrase Norvig's Latency numbers a programmer should know, if we imagine a computer that executes 1 CPU instruction every second, it would take it _days_ to read from RAM.

I think the author probably misread the numbers. If CPU executed 1 instruction every second, it would take just 1—2 minutes to read from uncached RAM, no need to be overly dramatic.

Overall, this reads to me like a very young programmer trying to convince themselves to learn Rust because he heard it's cool, not an objective evaluation. And I'm totally on board with that, whatever convinces you, just learn new things!

jplusequalt 22 hours ago [-]
>There is an apocryphal story about Euler in elementary school solving all the math problems that the teacher gave to the class in a jiffy, so the teacher tells him to sum up the numbers to a thousand to get him to stop pestering for more. The expectation was that Euler would go through the numbers "imperatively", like C, summing them up. Instead, what Euler did was discover the summation formula and solved it "declaratively" like Haskell, in one go, as an equation.

I've heard this story be accounted to Gauss, not Euler.

n4r9 9 hours ago [-]
Yes, it's Gauss. In fact the technique is sometimes known as "Gaussian summation". The New Scientist has an article where the author chases down early references to the story: https://www.americanscientist.org/article/gausss-day-of-reck...

The earliest reference is a biography of Gauss published a year after his death by a professor at Gauss' own university (Gottingen). The professor claims that the story was "often related in old age with amusement and relish" by Gauss. However, it describes the problem simply as "the summing of an arithmetic series", without mention of specific numbers (like 1-100). Also, it was posed to the entire classroom - presumably as a way to keep them busy for a couple of hours - rather than as an attempt to humiliate a precocious individual.

lblume 8 hours ago [-]
Yes. In Germany the formula n(n+1)/2 is actually called the Gaussian sum formula, or even the "small Gauss". [0]

[0] https://de.wikipedia.org/wiki/Gaußsche_Summenformel

lmm 16 hours ago [-]
If they're serious about their criteria they should go with OCaml (or maybe, like, Swift, or any of dozens of languages in that space).

(Of course they actually do want Haskell but they probably need to get there gradually)

frou_dh 7 hours ago [-]
It doesn't use curly-braces driven syntax, so will probably be accused of being "complex" and dismissed.
zem 16 hours ago [-]
right! the table at the end just screamed "use ocaml and be happy"
legobmw99 16 hours ago [-]
nobody gives OCaml a thought in these discussions. It’s such a wonderful language!
tayo42 11 hours ago [-]
Does ocaml have a mature ecosystem of libraries and dependencies? And easy way to manage them? Even rust with all its hype lacks in this area imo.
lmm 10 hours ago [-]
No, and that's part of why I use Scala instead. But this person is looking to make a binary to do a small thing on their desktop and does not list dependency management among their criteria.
sealeck 7 hours ago [-]
It doesn't have a great ecosystem of libraries, but it _does_ have a good way to manage them: https://dune.build/, which as I understand it was developed at Jane Street (lots of code and dependencies) and is now open source.

> Even rust with all its hype lacks in this area imo.

This is surprising to me! I find that Rust has pretty excellent tooling, and Cargo is substantially better than the package manager in most other languages...

tayo42 2 hours ago [-]
I didn't write that clearly. I meant the ecosystem still isn't mature. Agree with you rust tooling is major positive for the language
10 hours ago [-]
myaccountonhn 8 hours ago [-]
Depends on what you're doing and how you structure your program. It certainly does not match the ecosystem of Go, Rust or C++
tux3 23 hours ago [-]
>To paraphrase Norvig's Latency numbers a programmer should know, if we imagine a computer that executes 1 CPU instruction every second, it would take it days to read from RAM.

It's a detail, but this is a little bit off. RAM latency is roughly around ~100ns, CPUs average a couple instructions per cycle and a few cycles per ns.

Then in the analogy, a stall on RAM is about a 10 minute wait; not quite as bad as losing entire days.

jlokier 21 hours ago [-]
In current machines, that's way off depending on how you choose to count "1 CPU instruction" for the metaphor.

Take Apple's latest laptops. They have 16 CPU cores, 12 of those clocking at 4.5 GHz and able to decode/dispath up to 10 instructions per cycle. 4 of those clocking at 2.6 GHz, I'm not sure about their decode/dispatch width but let's assume 10. Those decoder widths don't translate to that many instructions-per-cycle in practice, but let's roll with it because the order of magnitude is close enough.

If the instructions are just right, that's 824 instructions per nanosecond. Or, roughly a million times faster than the 6502 in the Apple-II! Computers really have got faster, and we haven't even counted all the cores yet.

Scaling those to one per second, a RAM fetch taking 100ns would scale to 82400 seconds, which 22.8 hours, just short of a day.

Fine, but we forgot about the 40 GPU cores and the 16 ANE cores! More instructions per ns!

Now we're definitely into "days".

For the purpose of the metaphor, perhaps we should also count the multiple lanes of each vector instruction on the CPU, and lanes on the GPU cores, as if thery were separate processing instructions.

One way to measure that, which seems fair and useful to me, is to look at TOPS instead - tera operations per second. How many floating-point calculations can the processor complex do per second? I wasn't able to find good figures for the Apple M4 Max as a whole, only the ANE component, for which 38 TOPS is claimed. For various reasons tt's reasonable to estimate the GPU is the same order of magnitude in TOPS on those chips.

If you count 38 TOPS as equivalent to "CPU instructions" in the metaphor, then scale those to 1 per second, a RAM fetch taking 100ns scales to a whopping 43.9 days on a current laptop!

tux3 19 hours ago [-]
If you're counting all instruction executing in parallel with the maximum on-paper IPC on all CPUs, accelerators, and GPUs, the number your get has no clear relation to RAM latency. It really is comparing apples and oranges.

This scenario where all your 16 cores are doing 10 instructions per clock assumes everything is running without waiting, at full instruction-level and CPU-level parallelism. It's a measure of the maximum paper throughput when you're not blocked waiting on memory.

You could compare that to the maximum throughput of the RAM and the memory subsystem, and that would give you meaningful numbers (for instance, how many bytes/cycle can my cores handle? How many GB/s can my whole system process?).

Trying to add up the combined throughput of everything you can on one side and the latency of a single fetch on the other side will give you a really big number, but as a metaphor it will be more confusing than anything.

renewiltord 17 hours ago [-]
This seems like the classic 9 women making a baby in 1 month. Even if the CPU can execute 824 instructions per nanosecond, it can't execute 1 instruction in 1/824 nanoseconds. You can't mix throughput and latency like that.
tines 22 hours ago [-]
Very cool, I wish the author good luck! I've been writing a compiler in Rust for a few months now and I absolutely love it. The ways it solves most of the problems it addresses feel like the "right" way to do things.

There are some things that feel a little weird, like the fact that often when you want a more complex data structure you end up putting everything in a flat array/map and using indices as pointers. But I think I've gotten used to them, and I've come up with a few tricks to make it better (like creating a separate integer type for each "pointer" type I use, so that I can't accidentally index an object array with the wrong kind of index).

Rust is one of those languages that change how you think, like Haskell or Lisp or Forth. It won't be easy, but it's worth it.

lenkite 13 hours ago [-]
The best way to use Rust is to circumvent use of the borrow-checker and lifetimes and use indices everywhere! Suddenly, it becomes more pleasant and easy to refactor :).
genshii 22 hours ago [-]
This hits close to home. TypeScript is also my language of choice for 90% of the software I write. I agree with the author that TypeScript is very close to the perfect level of abstraction, and I haven't seen another language with a type system that's nearly as enjoyable to use. Of course, TS (any by extension JS) obviously has its issues/complications. Bun solves a lot of the runtime-related issues/annoyances though.

For the other 10% software that is performance-sensitive or where I need to ship some binary, I haven't found a language that I'm "happy" with. Just like the author talks about, I basically bounce between Go and Rust depending on what it is. Go is too simple almost to a fault (give me type unions please). Rust is too expressive; I find myself debugging my knowledge of Rust rather than the program (also I think metaprogramming/macros are a mistake).

I think there's space in the programming language world for a slightly higher level Go-like language with more expressiveness.

daxfohl 22 hours ago [-]
I'm surprised ocaml doesn't have more market share here. Native, fast, robust type system, GC, less special syntax than rust, less obtuse than Haskell.
pragmatic 18 hours ago [-]
No libraries.

All the niche languages have a chicken and egg problem.

Only way around that is to be able to piggy back on C or JavaScript or Java.

daxfohl 18 hours ago [-]
Yeah, though when I say that I mean more like I'm surprised the ecosystem itself hasn't matured more. Rust and Go have both built solid ecosystems up from scratch, but it's a shame that OCaml hasn't since it's a nice middle ground between the two.
andrewflnr 17 hours ago [-]
I really considered one of the BuckleScript/js_of_ocaml languages for my current frontend project, but went with Typescript for basically conservatism/conventionalism reasons. I was already taking some arguably unnecessary technical risks on that project and wasn't sure I could afford more. I agree that I really wish OCaml or something close to it was mainstream.
lostmsu 12 hours ago [-]
Until recently it did not have multithreading
ashishb 22 hours ago [-]
TypeScript is good as a language. You can't generate static binaries out of it (except Docker images) and that itself is a deal breaker.
skybrian 11 hours ago [-]
You can’t? I haven’t used it, but what’s wrong with “deno compile?”

https://docs.deno.com/runtime/reference/cli/compile/

shim__ 8 hours ago [-]
Sounds like electron but for cli
chainwax 1 minutes ago [-]
I does indeed bundle it's runtime into the executable, but this is not dissimilar from Go bundling it's gc into every executable.

Granted a compiled hello world Go program will be <2mb while a similar compiled Deno program will be more like 70. I imagine many usecases may not have a storage constraint though, and in that case why not?

genshii 22 hours ago [-]
Yeah, that's definitely a huge drawback. Bun lets you get pretty close though with the `--compile` flag: https://bun.sh/docs/bundler

Too bad the binaries are 60MB at a minimum :(

wk_end 22 hours ago [-]
I'd love to see someone develop a compiler for TypeScript that got, say, Ocaml-like performance. There's a bunch of reasons why that'd be tough though - you'd probably want a language very-similar-to-but-not-quite-like-TypeScript.
abhijat 2 hours ago [-]
There used to be reasonml but I'm not sure how active it is these days.
williamstein 22 hours ago [-]
This is probably the closest thing to that: https://www.assemblyscript.org/
wk_end 22 hours ago [-]
The documentation is really incomplete. It's not at all clear how similar to TypeScript this is...does this support structural subtyping? Type manipulation? Iterators/generators? Async?
ellg 22 hours ago [-]
its called C#
wk_end 22 hours ago [-]
C# is a lovely language but it's not that similar to TypeScript. The big distinction (for me) is that it's nominally-typed and much more rigid. Unless things have changed a lot since I last touched it (admittedly a while ago):

You can't synthesize ad hoc union types (https://www.typescriptlang.org/docs/handbook/2/everyday-type...)

There's no notion of literal types (https://www.typescriptlang.org/docs/handbook/2/everyday-type...)

There's no type narrowing (which gets you a kind of sum type) (https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...)

Most of the type maniuplation features (keyof, indexed access types, conditional types, template literal types...) are missing (https://www.typescriptlang.org/docs/handbook/2/types-from-ty...)

pragmatic 18 hours ago [-]
Close but not quite.
Tade0 22 hours ago [-]
I mean you can, they're just inappropriately large.
Sophistifunk 14 hours ago [-]
I very much enjoy reading and writing TS code. What I don't enjoy is the npm ecosystem (and accompanying mindset), and what I can't stand is trying to configure the damn thing. I've been doing this since TSC was first released, and just the other day I wasted hours trying to make a simple ts-node command line program work with file-extension-free imports and no weird disagreements between the ts-node runner and the language server used by the editor.

And then gave up in disgust.

Look, I'm no genius, not by a long shot. But I am both competent and experienced. If I can't make these things work just by messing with it and googling around, it's too damned hard.

mrkeen 1 hours ago [-]
I encountered this trying out PureScript. Looks like a good language, but I gave up after a couple of trips through npm, bower, yarn...
childintime 8 hours ago [-]
Fully agree. Try bun.
WorldMaker 2 hours ago [-]
Or deno.
renewiltord 17 hours ago [-]
I recently came to a production Typescript codebase and it took minutes to compile. Strangely, it could not behave correctly without a linter rule `no-floating-promises` but the linter also took minutes to lint the codebase. It was an astounding exercise in patience. Faster linters like oxlint exist but they don't have a notion of cross-file types so `no-floating-promises` is impossible on them.

The worst part is that `no-floating-promises` is strange. Without it, Knex (some ORM toolkit in this codebase) can crash (segfault equivalent) the entire runtime on a codebase that compiles. With it, Knex's query builders will fail the lint.

It was confusing. The type system was sophisticated enough that I could generate a CamelCaseToSnakeCase<T> type but somehow too weak to ensure object borrow semantics. Programmers on the codebase would frequently forget to use `await` on something causing a later hidden crash until I added the `no-floating-promises` lint, at which point they had to suppress it on all their query builders.

One could argue that they should just have been writing SQL queries and I did, but it didn't take. So the entire experience was fairly nightmarish.

WorldMaker 2 hours ago [-]
Promise<T> is not T and Typescript's type system will catch that in most cases with a simple type assertion (add a type to the function return; add a `satisfies` check somewhere).

If it isn't catching it often enough, without a lot more extra type assertions, you may be missing a Typescript strict flag like noImplicitAny. (In general I find that I prefer the full `"strict": true` in the compilerOptions of tsconfig.json and always write for all strict checks.)

Also if your codebase is still relying on "explicit" `any`, try `unknown`.

Also, yeah Knex doesn't look like the best ORM for a Typescript codebase. Typescript support in Knex is clearly an after thought, and the documentation admits it:

> However it is to be noted that TypeScript support is currently best-effort.

rob74 3 hours ago [-]
Oh, another one of those articles where people try to logically explain why they absolutely need to learn Rust and no other language will do. This time, even with religious connotations (https://en.wikipedia.org/wiki/God-shaped_hole). I mean, if you want to learn Rust, good for you, go ahead, no need to write a whole blog post rationalizing your decision!
liampulles 1 hours ago [-]
I like the "lower level of abstraction" of Go. It was a transition coming from writing Spring Boot Java code to having to actually implement the "magic", but I like that I can clearly see the control flow of things in Go.

Out of all the languages I've used, Go programs are the ones that have the highest percentage chance of working "first try". I think that has a lot to do with the plain and strongly typed style.

pavlov 8 hours ago [-]
> “Rust, from what I've heard, has a similar abstraction level as TypeScript, perhaps even closer to Haskell but that's good, I could do with a bit more help from the compiler. But it requires me to manage memory and lifetimes, which I think is something the compiler should do for me.”

The Rust compiler does manage memory and lifetimes. It just manages them statically at compile-time. If your code can’t be guaranteed to be memory-safe under Rust’s rules, it won’t compile and you need to change it.

PartiallyTyped 8 hours ago [-]
You can use runtime ownership structures like ref cells, arcs, etc as well.
pavlov 7 hours ago [-]
Sure. The part that people often complain about is that they have to make these decisions, rather than having a runtime default picked for them by the language.
ashishb 22 hours ago [-]
I actively seek out tools written in static languages. They are less fragile and have a longer shelf life.

https://ashishb.net/programming/maintaining-android-app/

Expurple 21 hours ago [-]
I agree so hard. That's why I use Hugo for my website. Speed was always only a bonus
brian_herman 14 hours ago [-]
Deno creates binary files from typescript. https://deno.com/blog/deno-compile-executable-programs
alt187 7 hours ago [-]
You may want to try the criminally underrated OCaml.

It's fast, compiles to native code AND javascript, and has garbage collection (so no manual memory management).

As an added bonus, you can mix Haskell-like functional code and imperative code in a single function.

spooneybarger 14 hours ago [-]
I've written a ton of C in my life and a C lot of Go and I was rofl at the "no spooky action at distance" lines.

This was brilliant performance art. Bless your heart Dear Author, I adore you.

blamestross 14 hours ago [-]
Everyone who has said the phrase "Spooky action at a distance" has been proven wrong :)
j-krieger 23 hours ago [-]
I write Gleam for this. A rust like language on the erlang VM. It's neat, but not widely used.
liampulles 1 hours ago [-]
I've written some Gleam as an exploration, and I liked it, but the "not widely used" thing is a concern. I need there to be some well maintained libraries for enterprise stuff.

I know this is not the fault of the language, and that is unfortunate.

giancarlostoro 23 hours ago [-]
I have been wanting to use Gleam more, but I havent found the right project or time. I can prototype drastically easier using Python.
slowcache 22 hours ago [-]
Odin has been really growing on me lately as a language that checks all of those boxes. String types, first class allocators, built in tests, a batteries included philosophy, and ease of use are some of the things that really drew me towards it.

I really wanted to like rust and I wrote a few different small toy projects in it. At some point knowledge of the language becomes a blocker rather than knowledge the problem space, but this is a skill issue that I'm sure would lessen the more I used it.

What really set me off was how every project turned into a grocery list of crates that you need to pull in in order to do anything. It started to feel embarrassing to say that I was doing systems programming when any topic I would google in rust would lead me to a stack overflow saying to install a crate and use that. There seemed to be an anti-DIY approach in the community that finally drew me away.

deathanatos 10 hours ago [-]
> String types

It's a byte string.

> rune is the set of all Unicode code points.

We copied the awful name from Go … and the docs are wrong.

Five different boolean types?

Zero values. (Every value has some default value, like in Go.)

Odin also includes the Billion Dollar Mistake.

> There seemed to be an anti-DIY approach in the community that finally drew me away.

It's a "let a thousand flowers bloom" approach, at least until the community knows which design stands a good chance of not being a regretted addition to the standard library.

ramity 6 hours ago [-]
I really want to love rust, and I understand the niches it fills. My temporary allegiance with it comes down to performance, but I'm drawn by the crate ecosystem and support provided by cargo.

What's so damning to me is how debilitatingly unopinionated it is during situations like error handling. I've used it enough to at least approximate its advantages, but strongly hinting towards including a crate (though not required) to help with error processing seems to mirror the inconvenience of having to include an exception type in another language. I don't think it would be the end of the world if it came with some creature comforts here and there.

klntsky 22 hours ago [-]
What's the difference between anti-DIY and "batteries included"?
tialaramex 9 hours ago [-]
If Ginger Bill thinks some niche feature might be useful then baking it into Odin is "batteries included" if not, having it would be anti-DIY. This is very much Bill's language. If what you're looking for is that auteur stamp you won't find it in C or Rust or Typescript but you will find it in languages like Jai, Odin, Hare, maybe Zig.

If that creator's vibe happens to match yours this could be beautiful, at least for personal projects. It's hard to imagine this scaling. A triple A studio hiring panel: "You've applied for a job but we write only Jai here. We notice you haven't submitted any obsessive fan art about Jonathan Blow. Maybe talk us through the moment you realised he was right about everything?"

mrkeen 57 minutes ago [-]
Batteries included is a fat, opinionated standard library.

I think for some devs, if you import from the standard library, that somehow counts as DIY, whereas if you import from libraries that aren't distributed with the compiler, it's anti-DIY.

Expurple 21 hours ago [-]
re:crates https://web.archive.org/web/20250420085150/https://wiki.alop...
turboponyy 1 days ago [-]
> While I can jump through hoops to compile JavaScript into a binary, such wouldn't feel "solid". And the very point of writing a native program in the first place is to make it feel solid

You can use Bun to compile to native binaries without jumping through hoops. It's not mature, but it works well enough that we use it at work.

genshii 22 hours ago [-]
It's definitely nice for certain use cases. I just wish the binaries weren't so huge (~60MB + your actual source code).
flohofwoe 8 hours ago [-]
My two 'language poles' are Typescript as the 'north pole', and C as the 'south pole', with Python, C++, Zig (and to a lesser extent, Rust and Odin) placed somewhere along the latitudes.

I think that of all those options, Typescript and Zig feel closest related. Zig has that same 'lightness' when writing code as Typescript and the syntax is actually close enough that a Typescript syntax highlighter mostly works fine for Zig too ;)

Sophistifunk 14 hours ago [-]
Sounds like a Zig-shaped hole to me ;-)
Expurple 12 hours ago [-]
They complain that Go is too low-level for their needs. Zig, with its explicit allocators, is definitely even lower-level.

Rust seems low-level too, but it isn't the same. It allows building powerful high-level interfaces that hide the complexity from you. E.g., RAII eliminates the need for explicit `defer` that can be forgotten

Sophistifunk 11 hours ago [-]
True, but I think the "low-level" complaint against Go in the article was just referring to all the stupid repetitive ceremony required for error handling, which Zig mostly skips over.
Expurple 10 hours ago [-]
Fair enough. That's what they seem to be saying.

But then I want to chime in and argue that the repetitive syntax isn't even close to being the main problem with Go: https://home.expurple.me/posts/go-did-not-get-error-handling...

sgt 10 hours ago [-]
So, while on that subject; does Zig get error handling right?
lblume 8 hours ago [-]
It does seem to: https://pedropark99.github.io/zig-book/Chapters/09-error-han...

However errors do not seem to commonly wrapped, tagged or contextualized as is the case in Rust. This might weight lower verbosity as more important than extremely structured error handling which definitely constitutes an interesting approach.

Expurple 10 hours ago [-]
Idk, I'm not familiar enough with Zig to say
osigurdson 3 hours ago [-]
I think it does.
tptacek 22 hours ago [-]
I don't think you need an elaborate process of elimination when one of your axioms is "must manage memory manually".
yccs27 22 hours ago [-]
> Memory management was indeed the sore sticking point, why Rust hadn't appealed to me earlier.

The author doesn't want manual memory management, but still decides to go with Rust.

tptacek 22 hours ago [-]
Their rubric is literally just 3 items: "native compilation", "abstractions", and "manual memory management". Had they put that little table at the front of the article, there wouldn't even need to be an article: the table basically says "I'm going to use Rust". That's fine!
JoelMcCracken 21 hours ago [-]
The author really doesn't want to do manual memory management. The table is there to summarize things discussed, but he never says he wants to do manual memory management. I just went back and checked. If you do find something that indicates that, I'd appreciate you pointing it out to me, because idgi
tptacek 20 hours ago [-]
I read the article twice, but I could also just be wrong; it's not that big a deal.
Expurple 20 hours ago [-]
No problem, get some sleep in that case :D
ignoramous 17 hours ago [-]
> The table is there to summarize things discussed, but he never says he wants to do manual memory management.

  But it requires me to manage memory and lifetimes, which I think is something the compiler should do for me.
The author wants the compiler to do memory management? How does Rust achieve this?
adastra22 11 hours ago [-]
It is better to think of Rust having explicit memory management, rather than manual memory management. C/C++ has manual memory management: the burden is on you to do it correctly. If you fuck up, your program will have bugs. Rust requires that your code be explicit about memory issues, but the compiler works with you to achieve that. if your code compiles it is correct; if it doesn't compile, the error points out what needs to be fixed. When I write Rust it rarely feels like I am taking on that burden myself.
steveklabnik 17 hours ago [-]
The compiler works with you to ensure that you’re not misusing memory. If you make a mistake, it will let you know.

You’re not often manually managing memory in the same sense as other low-level languages. You’re not invoking malloc and free directly. Thanks to ownership, when you do allocate, Rust will call free for you.

It’s somewhere in between fully manual and fully automatic. It can feel more like one or the other based on what you’re doing, but most of the time, in average, it feels like automatic.

Expurple 21 hours ago [-]
I don't think you need an elaborate comment when one of your axioms is "must not read the post that you're responding to".
eviks 11 hours ago [-]
He's saying exactly the opposite

> Rust... But it requires me to manage memory and lifetimes, which I think is something the compiler should do for me.

bee_rider 22 hours ago [-]
I’m not sure what his abstraction column really means, nuts and bolts-wise. But, Fortran is native, you get to allocate your own memory, and it has object oriented features (maybe that’s abstraction).
pron 22 hours ago [-]
> And the very point of writing a native program in the first place is to make it feel solid.

What does that mean, and what is it about native programs (i.e. programs AOT-compiled to machine code) that makes them feel solid? BTW, such programs are often more, not less, sensitive to OS changes.

> realizing that I was just spawning complexity that is unrelated to the problem at hand

Wait till you use Rust for a while, then (you should try, though, if the language interests you).

For me, the benefit of languages with manual memory management is the significantly lower memory footprint (speed is no longer an issue; if you think Haskell and Go are good enough, try Java, which is faster). But this comes at a price. Manual memory management means, by necessity, a lower level of abstraction (i.e. the same abstraction can cover fewer implementations). The price is usually paid not when writing the first version, but when evolving the codebase over years. Sometimes this price is worth it, but it's there, and it's not small. That's why I only reach for low level languages when I absolutely must.

Expurple 21 hours ago [-]
> such programs are often more, not less, sensitive to OS changes.

You may be technically correct that they are more sensitive to the kernel interface changes. But the point is that native, static binaries depend only on the kernel interface, while the other programs also depend on the language runtime that's installed on that OS. Typical Python programs even depend on the libraries being installed separately (in source form!)

pron 21 hours ago [-]
> But the point is that native, static binaries depend only on the kernel interface

Many binaries also depend on shared libraries.

> while the other programs also depend on the language runtime that's installed on that OS

You can (and probably should) embed the runtime and all dependencies in the program (as is easily done in Java). The runtime then makes responding to OS selection/changes easier (e.g. musl vs glibc), or avoids less stable OS APIs to begin with.

Expurple 21 hours ago [-]
> Many binaries also depend on shared libraries.

Yeah, and those are also the opposite of "solid" :) That's why I qualified with "static". I'm so glad that Go and Rust promote static linking as the default (ignoring glibc).

> You can (and probably should) embed the runtime and all dependencies in the program (as is easily done in Java).

Congrats to the Java team and users, then. That makes it similar to the Go approach to binaries and the runtime, which I approve

pron 20 hours ago [-]
> Yeah, and those are also the opposite of "solid" :)

So if that's what the author meant by "solid", i.e. few environmental dependencies, then it's not really about "native" or not, but about how the language/runtime is designed. Languages that started out as "scripting" languages often do rely on the environment a lot, but that's not how, say, Java or .NET work.

> I'm so glad that Go and Rust promote static linking as the default (ignoring glibc).

That doesn't work so well (and so usually not done) once you have a GUI, although I guess you consider the GUI to be part of the kernel.

pjmlp 9 hours ago [-]
Even C has a runtime, even if tiny one.
Expurple 8 hours ago [-]
I meant a runtime that has to be installed separately. It's possible to statically link a C runtime if you use musl, for example
pjmlp 8 hours ago [-]
You mean like it happens on many OSes that aren't GNU/Linux?

A language runtime remains one, independently on how it was linked into the binary.

A language runtime are the set of operations that support the language semantics, which in C's case are everything that happens before main(), threading support (since C11), floating point emulation (if needed), execution hooks for running code before and after main(), delayed linking, and possibly more, depending on the compiler specific extensions.

Expurple 8 hours ago [-]
You're being pedantic and trying to argue as if I misunderstand language runtimes and am speaking against language runtimes in general. That's not true. I qualified "the language runtime that's installed on that OS" from the beginning.
pjmlp 7 hours ago [-]
Like on Windows for the Universal C Runtime?

I can give other non-GNU/Linux examples.

Expurple 7 hours ago [-]
I don't have any negative experience with that one, but I remember having to manually install various versions of the Windows C++ runtimes to get an app working
Mawr 18 hours ago [-]
> For me, the benefit of languages with manual memory management is the significantly lower memory footprint (speed is no longer an issue; if you think Haskell and Go are good enough, try Java, which is faster).

... what? Speed is no longer an issue? Haskell and Go? ??? How'd we go from manual memory management languages to Haskell and Go and then somehow to Java? Gotta plug that <my favorite language> somehow I guess...

It seems to me you have a deep misunderstanding of performance. If one program is 5% faster than another but at 100x memory cost, that program is not actually more performant. It just traded all possible memory for any and all speed gain. What a horrible tradeoff.

This thinking is typical in Java land [1]. You see: 8% better performance. I see: 28x the memory usage. In other words, had the Rust program been designed with the same insane memory allowance in mind as the Java program, it'd wipe the floor with it.

[1]: https://old.reddit.com/r/java/comments/n75pa0/java_beats_out...

ignoramous 17 hours ago [-]
> What does that mean, and what is it about native programs (i.e. programs AOT-compiled to machine code) that makes them feel solid? BTW, such programs are often more, not less, sensitive to OS changes

TFA also concludes

  Since I want native code ...
I think by "solid" they mean as close to metal as possible, because, as you suggest, one can go "native" with AOT. With JS/TS (languages TFA prefers), I'm not sure how far WASM's AOT will take you ... Go (the other language TFA prefers) even has PGO now on top of "AOT".
junon 8 hours ago [-]
> Rust, from what I've heard, has a similar abstraction level as TypeScript, perhaps even closer to Haskell but that's good, I could do with a bit more help from the compiler. But it requires me to manage memory and lifetimes, which I think is something the compiler should do for me.

Eh.... yeah? I suppose technically? But not _really_. Rust gives you the option to do that. But most programs outside of "I'm building an operating system" don't really require thinking too hard about it.

It's not like C where you're feeding memory manually, or like C++ where you have to think about RAII just right.

DarkNova6 23 hours ago [-]
The table in the end sums it up nicely.

Rust allows low level programming and static compilation, while still providing abstraction and safety. A good ecosystem and stable build tools help massively as well.

It is one of the few languages which managed to address a real life need in novel ways, rather than incrementing on existing solutions and introducing new trade offs.

sesm 8 hours ago [-]
> I once spent an entire year in the heaven of C++, walking around in a glorious daze of std::vector and RAII, before one day snapping out of it and realizing that I was just spawning complexity that is unrelated to the problem at hand.

Good luck to the author with trying Rust. I hope he writes an honest experience report.

JoelMcCracken 22 hours ago [-]
Wow, a lot of stuff in here surprises me. C definitely can/does have spooky at a distance. Just share a pointer to a resource with something else and enjoy the spooky modifications. Changes are local as long as you program that way, but sometimes it can be a bit not-obvious that this is happening.

regarding redefining functions, what could the author mean? using global function pointers that get redefined? otherwise redefining a function wouldn't effect other modules that are compiled into separate object files. confusing.

C is simple in that it does not have a lot of features to learn, but because of e.g. undefined behavior, I find its very hard to call it a simple language. When a simple bug can cause your entire function to be UB'd out of existence, C doesn't feel very simple.

In haskell, side effects actually _happen_ when the pile of function applications evaluate to IO data type values, but, you can think about it very locally; that's what makes it so great. You could get those nice properties with a simpler model (i.e. don't make the langague lazy, but still have explicit effects), but, yeah.

The main thing that makes Haskell not simple IMO is that it just has such a vast set of things to learn. Normal language feature stuff (types, typeclasses/etc, functions, libraries), but then you also have a ton of other special haskell suff: more advanced type system tomfoolery, various language extensions, some of which are deprecated now, or perhaps just there are better things to use nowadays (like type families vs functional dependencies), hierarchies of unfamiliar math terms that are essentially required to actually do anything, etc, and then laziness/call-by-name/non-strict eval, which is its own set of problems (space leaks!). And yes, unfamiliar syntax is another stumbling block.

IME, Rust is actually more difficult than Haskell in a lot of ways. I imagine that once you learn all of the things you need to learn it is different. The way I've heard to make it "easier" is to just clone/copy data any time you have a need for it, but, what's the point of using Rust, then?

I wonder if the author considered OCaml or its kin, I haven't kept track of whats all available, but I've heard that better tooling is available and better/more familiar syntax. OCaml is a good language and a good gateway into many other areas.

There are some other langs that might fit, like I see nim as an example, or zig, or swift. I'd still like to do more with swift, the language is interesting.

pjmlp 8 hours ago [-]
When people say C is simple, besides everything that you point out, apparently they never learned anything beyond the classical K&R C book (ANSI/ISO C edition), and are stuck in a C89 mindset without any kind of compiler extensions.
tines 22 hours ago [-]
> Wow, a lot of stuff in here surprises me. C definitely can/does have spooky at a distance. Just share a pointer to a resource with something else and enjoy the spooky modifications. Changes are local as long as you program that way, but sometimes it can be a bit not-obvious that this is happening.

I think the author means that the language constructs themselves have well-defined meanings, not that the semantics don't allow surprising things to happen at runtime. Small changes don't affect the meaning of the entire program. (I'm not sure I agree that this isn't the case for e.g. Haskell as well, I'm just commenting on what I think the author means.)

> IME, Rust is actually more difficult than Haskell in a lot of ways. I imagine that once you learn all of the things you need to learn it is different.

Having written code in both, Rust is quite a lot easier than Haskell for a programmer familiar with the "normal" languages like C, C++, Python, whatever. The pure functionality of Haskell is quite a big deal that ends up contorting my programs into weird poses, e.g. once you run into the need to compose Monads the complexity ramps way up.

> The way I've heard to make it "easier" is to just clone/copy data any time you have a need for it, but, what's the point of using Rust, then?

Memory safety. And the fact that this is the example of Rust complexity just goes to show what a higher level Haskell's difficulty is.

JoelMcCracken 21 hours ago [-]
Thanks for explaining what you think the author meant; i meant to reiterate that I was trying to actually understand as opposed to argue, but I forgot; I think sometimes "hey I don't get what you're saying because of reasons XYZ..." comes across as "I think you're wrong".

Composing monads is another one of those painful parts of haskell. I remember being so frustrated while learning Haskell that there was all of this "stuff" to learn to "use monads" but it seemd to not have anything to _do_ with `Monad`, and people told me what I needed to know was `Monad`. Someday I wanna write all that advice I wish I had received when learning Haskell. A _lot_ of it will be about dealing with general monad "stuff".

The thing that frustrated me in Rust coming from something like Ruby was how frequently I could not _do_ a very straightforward thing, because, for example, some function is a fnOnce instead of fnMulti, or the other way around, or whatever. Here's some of the experience from that time https://joelmccracken.github.io/entries/a-simple-web-app-in-.... It became clear to me eventually that some very minor changes in requirements could necessitate massive changes in how the whole data model is structured. Maybe eventually I'd get good enough at rust that this wouldn't be a huge issue, but I had no way of seeing how to get to that point from where I was.

In contrast, I can generally predict when some requirement is going to necessitate a big change in haskell: does it require a new side effect? if so, it may need a big change. If not, then it probably doesn't. But, I've found it surprisingly easy to make big changes from the nice type system.

I really don't get when rust folks claim "memory safety" like this; we've had garbage collection since 1959. Rust gives you memory safety with tight control over resource usage; memory safety is an advantage that Rust has over C or C++, but not over basically every other language people still talk about.

If you just clone/copy every data structure left and right, then you're at a _worse_ spot than with garbage collection/reference counting when it comes to memory usage. I _guess_ you are getting the ability to avoid GC pauses, but, why not use a reference counted language if that's the problem? copy/clone data all of the time can't be faster than the overhead from a reference counting, can it??

In haskell, I did find that once I understood the various pieces I needed to work with, actually solving problems (e.g. composing monads) is much easier. I don't generally have a hard time actually programming Haskell. All that effort is front-loaded though, and it can be hard to know exactly what you need to learn in order to understand some new unfamiliar thing.

Your preferring Rust over Haskell is totally fine BTW, I'm just trying to draw a distinction between something that's hard to _use_ vs something that's hard to _learn_. Many common languages are much harder to use IME; I feel like I have to think so hard all of the time about every line of code to make sure I'm not missing something, some important side effect that I don't know about that is happening at some function call. With Haskell, I can generally skim the code and find what's important quite quickly because of the type system.

I do plan to learn Rust at some point still whenever the planets align and I need to know something like it. Until then, there are so many other things that interest me, and not enough hours in the day. I still wonder if I have really missed out on some benefit from learning to think more about data ownership in programs.

Expurple 21 hours ago [-]
> frequently I could not _do_ a very straightforward thing, because, for example, some function is a fnOnce instead of fnMulti, or the other way around [..] It became clear to me eventually that some very minor changes in requirements could necessitate massive changes in how the whole data model is structured. Maybe eventually I'd get good enough at rust that this wouldn't be a huge issue, but I had no way of seeing how to get to that point from where I was.

I understand your frustration, and Rust does get too low-level sometimes (see https://without.boats/blog/notes-on-a-smaller-rust/). But the semantic difference between FnOnce and Fn is actually important. Fn consumes its environment and makes it unavailable later. This is an important property. When you don't want that, "just" use Fn, wrap everything in an Arc and clone everything (I understand that this is more ceremony than in other languages, and that can be unjustified).

> I really don't get when rust folks claim "memory safety" like this; we've had garbage collection since 1959.

Agree 100%! What Rust actually gives you is predictability, reliability and compile time checks, while still allowing to write relatively ergonomic imperative and "impure" code. And a sane ecosystem of tools that are designed to be reliable and helpful. I'm currently writing a post about this.

It also gives compile-time data race protection, which is still missing from some other memory-safe languages.

> I still wonder if I have really missed out on some benefit from learning to think more about data ownership in programs.

Yeah :) Affine types + RAII (ownership) allow you to express some really cool things, such as "Mutex<T> forces you to lock the mutex before accessing the T and automatically unlocks it when you're done", or "commits and rollbacks destroy the DatabaseTransaction and make it statically impossible to interact with", or "you'll never forget to run cleanup code for objects from external C libraries" (https://www.reddit.com/r/programming/comments/1l1nhwz/why_us...)

JoelMcCracken 21 hours ago [-]
I asked a bad question; what I meant was specifically thinking about ownership in terms of being required everywhere and to manage memory. But there are for sure real benefits I can see for _other_ properties in certain situations, like avoiding data races that might occur in Haskell. GHC added a linear type extension, for that matter (though AFAIK its still not very great to use). But some of that seems distinct from the question "do I benefit in some data modeling way from thinking about the ownership of memory in my program" as opposed to creating/sharing references whenever convenient.
Expurple 20 hours ago [-]
The deal with strict Rust-style references is that they solve mutable aliasing bugs (data races, iterator invalidation, all kinds of unexpected mutation really). That's the deal with Rust.

The whole "memory management" thing is mostly a historical accident. We could have a "smaller Rust" that auto-boxes values, has a runtime that handles reference cycles for you, and doesn't guarantee anything about the stack vs heap: https://without.boats/blog/notes-on-a-smaller-rust/

tines 21 hours ago [-]
Yep I feel you. Thanks!
blashyrk 2 days ago [-]
Someone really needs to show Nim to the author :). It checks all of their boxes and then some
elcritch 22 hours ago [-]
Yep it’s ideal for this sort of application without the headache of Rust. Plus it’s helpful it can compile to C,C++, or JavaScript. So take your pick.
Symmetry 22 hours ago [-]
I was thinking that too. There are many cases where you do want to manage memory yourself, and in that case you should likely use Rust or maybe Zig if you can choose your own tool. But if you don't want to manage your own memory Nim works nicely, though IMO it requires adherence to a style guide more than most languages.
timeon 10 hours ago [-]
Depends what you do but most of the time you do not need to do anything special about memory management in Rust. That is why people try to use it for other things then just system programming.
nektro 15 hours ago [-]
> If someone posts a patch or submits a PR to a codebase written in C, it is easier to review than any other mainstream language.

short-circuited reading this

outside1234 11 hours ago [-]
Rust is an amazing language once you get over the initial mental hurdle. An important thing to go in with: 99% of programs should not require you to manage lifetimes (‘a notation) If you find yourself doing this and aren’t writing a inner loop high performance library, back up and find another way. Usually this entails using a Mutex or Arc (or other alternatives based on the scenario) to provide interior mutability or multiple references. This statement might not make sense now but write it down for when it will.

I use Rust now for everything from CLIs to APIs and feel more productive in it end to end than python even.

TOGoS 15 hours ago [-]
I'm interested in the long piecewise elimination section. Presumably that's where they explain why not use Ocaml/Nim/yaddah yaddah.

If I were to write such a list, the answer would probably come down to "because I wanted to pick ONE and be able to stick with it, and Rust seems solid and not going anywhere." As much as Clojure and Ocaml are, from what I've heard, right up my alley, learning all these different languages has definitely taken time away from getting crap done, like I used to be able to do perfectly well with Java 2 or PHP 5, even though those are horrible languages.

d--b 5 hours ago [-]
> That leaves me with the following options — C, C++, Go, Rust.

> Technically, there are a lot more options, and I wrote a long section here about eliminating them piecewise, but after writing it I felt like it was just noise.

Uh? I am guessing OP doesn't like virtual machines maybe, cause Java and C# sound like something that fits what they want. Both support AoT compilation though now...

Also the assumption about Typescript to Wasm being not "solid" seems wrong.

I mean I find it super weird that the author's only option for "native typescript" is Rust.

self_awareness 9 hours ago [-]
I've seen this argument for years. "C is an easy language and it's easy to code review it.".

Maybe if you want to skip all the off-by-1 errors, double frees, overflows, underflows, wrong API usage, you don't need to maintain multiplatform build environment, and you don't support multiple architectures.

I mean, in this sense, assembly is even easier than C. Its syntax is trivial, and if that would be the only thing that matters, people should write assembly.

But they don't write assembly, because it's not the only thing that matters. So please stop considering C only in terms of easy syntax. Because syntax is the only thing that's easy in C.

thasso 10 hours ago [-]
A nice thing about C is that you can be pretty confident that you know all major footguns (assuming you spent some time reading about it). With languages that are young or complex there is a much greater chance you’re making a terrible mistake because you’re not aware of it.
tialaramex 4 hours ago [-]
It is nice that, unlike C++, the C language standard does list all the Undefined Behaviour (in Annex J.2), it's a pretty long list and IMO it's terrifying, not so much because of specifics like this:

"A searching or sorting utility function is called with an invalid pointer argument, even if the number of elements is zero"

But because of broad choices like:

"The execution of a program contains a data race"

"An object is referred to outside of its lifetime"

These are essentially categories of mistake we know programmers make, and in C the result is... Undefined Behaviour. No diagnostics, no exit, no errors, just throw your hands in the air and give up, anything might happen.

dwattttt 9 hours ago [-]
I'm yet to see someone be confident in this way on anything more than a trivial program, and be right. Just way too many footguns.

My personal memorable one was bit shifting 32bit values by varying amounts, and our test vectors all failing after a compiler update, because some of the shifts were by 32. Undefined behaviour.

B4uler5 22 hours ago [-]
If anyone reads this and like me fears the difficulty and complexity of rust, but still wants a language that is competitive in performance, works for system level programming as well as something more general purpose definitely give Swift a go.

Over the last year I’ve started to write every new project using it. On windows, on linux and mac.

It is honestly a wonderful language to work with. Its mature, well designed, has a lot of similarities to rust. Has incredible interop with C, C++, Objective-C and even Java as of this year which feels fairly insane. It also is ergonomic as hell and well understood by LLM’s so is easy to get into from a 0 starting point.

freeone3000 21 hours ago [-]
My major problem with Swift was its error messaging. “Error: -2.” Okay, well, I suppose I’ll attach a debugger and… it’s of absolutely no help, since it crashes internally to some function and I can’t get out a stacktrace.

Function calling is irksome, with implicit parameters and mandatory parameters vaguely mixing? And the typing is appalling - there are multiple bottom types with implicit narrowing casts, one of them being NSObject, so if you’re doing any work with the Apple APIs you end up with a mess.

We got it right with Java and Rust; C++ does a passable job; why Swift had to be as incomprehensible as typescript, I cannot fathom.

saagarjha 9 hours ago [-]
You should be able to get stack traces in the debugger, what issues did you run into?
freeone3000 3 hours ago [-]
I think the issue was an async boundary - I was able to trace back to CoreFoundationLibrary but the trail ended there. Also, lldb was unable to get locals? Which is something I’ve not seen happen…
tines 22 hours ago [-]
How tied to the Mac ecosystem is Swift these days?

Also, how is its type system and metaprogramming? Does it have type polymorphism, typeclasses, macros, etc?

B4uler5 21 hours ago [-]
Some of the functionalities in the core/foundation library don’t quite match between operating systems so sometimes you do need to put an “#if os(macos) do” etc, but for the most part its super straight forward working on one or another platform.

In terms of its language features it has all of those and more, sometimes too many in my opinion.

I personally favour languages that are clear in their vision. However at its core Swift is a highly performant language that is designed really well, has beautiful syntax, and in the last 5 or so years I have been impressed with its direction. It is being developed by a good team who listen to their community but not at the expense of the languages vision.

My favourite aspect of using it though is its versatility. Wether you’re working on embedded systems, a game, a web server, or even a static site generator, it always feels like the language is there to support your vision, but still give you the fine grained control you need to optimise for performance.

I also collaborate with a friend who is a Rust developer and he’s always super happy to work on a Swift project with me so I feel like that’s enough praise when you can pull a rest dev away from their beloved.

dlachausse 22 hours ago [-]
It supports Linux, Windows, and even Android. It has all those things and more.
dlachausse 22 hours ago [-]
Swift is the most underrated language in this space.