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.
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.
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.
> 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.
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.
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...
> 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)
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.
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.
> 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.
> "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.
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.