1. Removing structs and interfaces (in favor of what? Java-style classes and interfaces?) would make Rust more verbose, less of a systems language, and slower at runtime due to the indirect dispatch.
2. Changing the keyword "enum" to something else is a cosmetic change which would make as many users unhappy as it made happy. (We know, because we solicited feedback on the keyword before selecting it!)
3. I've addressed error handling in the sibling comment. There's basically no other way to do it that would be in line with Rust's goals as a systems language.
4. Rust would be a worse language if it lacked closures. Closure support is a basic feature in a modern language.
Sometimes I think CRust would be a good idea, the minimal ideas that I like from Rust just layered on top of C as a linter.
The point is that these ideas are not "weird"—they're just decisions taken straight from other languages.
I can't believe we have to defend closures, of all things, from the charge of being "weird" because a language from the 1970s didn't have them, but here we are.
> Sometimes I think CRust would be a good idea, the minimal ideas that I like from Rust just layered on top of C as a linter.
That is not possible. You can't just graft the borrow check from one language onto another language that was not designed for it. For example, C doesn't even have generics, and the lifetime system is designed around generics.
Honestly, I'm tired of responding over and over to the suggestion that we should have done Rust as a static analysis on C++. If you actually try to design such a system, it becomes obvious very quickly that it cannot work for systematic reasons.
Rust is a systems programming language that runs blazingly fast,
prevents segfaults, and guarantees thread safety.
In a systems programming language I can do without closures, without generics, without .... I would and will say the exact same thing about Swift which also provides closures, generics, ....Us systems programmer types, we tend to drag our knuckles a lot. Still we do have opposable thumbs for grasping tools and we're damned proud of that. Perhaps if and when we sufficiently evolve and attain an upright, high level posture we'll see the light. But for now, be happy that we're trainable if not educable.
Now please excuse me, but I have to go pick some low hanging fruit. That and worse is a lot better.
>What would you rather see? Exceptions
Go also backed into adding most of the machinery for exceptions by adding catch with unwind for panics. If you're going to have the unwinding machinery, why not have real exceptions?
Exceptions aren't so bad if you do them the way Python does, using a predefined exception hierarchy. Catching an exception catches everything in its subtree. Catching RuntimeError catches most of the things one can induce from outside the program. Python's WITH statement also has one of the few mechanisms which can gracefully handle an error on releasing a resource. Exceptions in C++, Java, and Javascript are more troublesome.
[1] https://github.com/rust-lang/rfcs/blob/master/text/1236-stab...
In any case, panics are not guaranteed to unwind or be catchable: they can be turned into aborts (yes, on the stable compiler). This means an arbitrary library using them for recoverable error handling is incorrect.
A WITH clause mechanism implies no dynamism.
Please be more specific with your complaints. Thanks.
Additionally, unless one is using explicitly typed exceptions (i.e. Java-style checked exceptions, very much unlike Python), it requires allocations, as it's not possible to statically determine the size of the error object to be returned.
Similarly, from an assurance/reliability point of view, the dynamically typed nature of non-checked exceptions is unfortunate.
(These second two are, I believe, the points I made last time we discussed Python's exceptions.)
"We'll also need a way to handle out-of-memory (OOM) conditions. The standard library calls the abort intrinsic, which just calls an illegal instruction to crash the whole program. The reason we abort and don't panic is because unwinding can cause allocations to happen, and that seems like a bad thing to do when your allocator just came back with "hey I don't have any more memory"."[1]
Allocating is definitely a big deal for a pervasive error handling! For instance, parsing a number might not succeed. Both the actual parsing and the failure cases are cheap, but not if the latter has to be indicated by allocating and unwinding and doing a pile of the dynamic checks up the stack.
Additionally, whether the language is able to operate in OOM isn't the only reason to choose an error handling scheme. However, even if it was, I don't think it applies directly to Rust: Rust-the-language (and Rust-the-core-library) can operate in an OOM condition, e.g. people run Rust code without needing a dynamic allocator at all. You are correct that some parts of the standard library don't handle OOM, but, using exceptions pervasively means that there is one error handling scheme for things that can rely on having an OS/malloc and one for things that don't need them.
> Besides, unwinding already causes allocation
Yes, and that is one reason why unwinding is not used as a pervasive error handling scheme in Rust: there are various fairly fundamental problems with it. It is one of the reasons why panics can be switched to abort.
Exceptions are not meant for "pervasive error", they are for exceptional execution paths. If an execution path is frequent enough, wether it's an "error" or not, then it should not be an exception.
I think this is the part of the argument that the "no exception camp" usually misses : there are multi type of errors (recoverable vs fatal, frequent vs unfrequent, expected vs unexpected), having exception is not about using exceptions for all of them
If exceptions are just "an option" that a library can choose when designing its interface, then you'll get a split ecosystem: a bunch of libraries that do use exceptions for error-handling, and then a separate bunch of libraries that do the same thing, but don't use exceptions for error handling, which is wasted, redundant effort.
Agreed. Point in case: Have a look at D and the discussions about their GC.
I haven't kept up much about the last year, but my general impression runs down to: If you want to use their standard library. There's gonna be garbage collection. Period. If you want to avoid it, enjoy implementing its functionality all by yourself.
With rust and exceptions you would likely not have a problem with the main ecosystem, but the same problem is bound to pop up with some big & famous library.
Edit: Similarly, exceptions are one of the reasons C++'s SG14 exists.
Rust's uses algebraic types to make things checked at compile time and uses operators like `?` to give you expressiveness while removing the overhead and annoyance of manually checking everything. It is a huge improvement.
Catchable with: https://hackage.haskell.org/package/base-4.9.1.0/docs/Contro...
If Rust is going to bill itself as a systems programming language, it needs these. I added them for my app:
extern {
#[link_name = "llvm.setjmp"]
pub fn setjmp(a: *mut i8) -> i32;
#[link_name = "llvm.longjmp"]
pub fn longjmp(a: *mut i8, b: i32) -> ();
}
It took me awhile to figure this out (and it barely works). There's a cleaner way (adding a wrapper) that is even more work.Sure, but what I meant is that most of the time userspace programs have nothing interesting they can do with the bus error (and, as I noted, standard C does not actually provide this option anyway). Regardless there are existing signal handling libraries that aren't that hard to use in Rust (you now describe one in your post), so I'm not sure why you're pointing to that as "something... that Rust lacks."
There's no way I'm aware of that setjmp / longjmp could possibly be integrated with safe Rust, but I agree that they should be available to unsafe code (in a way that doesn't require invoking llvm intrinsics).
And that's okay. There is a ton of space outside of that context, and still within the "systems" space that C and C++ could use a high quality competitor in, and Rust fits that bill nicely, in my view.
Rust the language is really composed of two almost identical subsets, "safe" one and "unsafe". Safe Rust is what you will normally see, governed by normal safety rules and abstractions. Unsafe Rust is not what you will normally see, but still governed by safety rules and abstractions, only with an escape hatch. What I feel is that safe Rust covers the higher-level subset of "systems programming" (predictable performance, strong abstraction and safety), while unsafe Rust covers the lower-level subset of "systems programming" (excellent performance, near-complete control over everything). And still they are almost identical, so that the abstraction made in unsafe Rust is usable in safe Rust, and that's ideally how it covers the entirety of "systems programming"---the only border is the abstraction itself. Of course, provided that we have enough supply for appropriate abstractions (the community is trying hard with several promising results though).
Rust programmers do like the safety guarantee of safe Rust and rarely talk about unsafe Rust, but I think unsafe Rust plays a large role in the possibility of Rust. It's much closer than the border between, say, "glue" languages and their implementation languages. We don't change the fact that we will sometimes have to bend the rules (it's probably impossible). Instead we let you bend the rules, but only when you are in the cage. And that cage is, while not immunable to every attack, really strong.
[1] The advocacy naturally advocates for something's possibility and not for something's success. So it is still correct that Rust still lacks some solutions for existing problems, though it's not inherent.
I think that's a misunderstanding of meaning. Rust's safety and unsafe are intrinsically linked. It's not that Rust is always safe, but that you have a well defined way to compartmentalize safe and unsafe portions of the program, and can use that to reason better about what's going on. In a way, talking about the safety of Rust is specifically talking about unsafe.
> then all of my carefully crafted error-handling routines would go up in flames
No just sandbox the code inside a try catch...
> pattern matching is the best method of clearly displaying intents and possible downfalls.
You are stating an opinions as a fact here.
This works fine if it's just a matter of rolling back a database transaction or releasing file handles (b/c someone else is doing all the work for you). But if you have more complicated invariants, your try catch has to handle restoring those invariants, no matter what path your code took.
The most important feature is that catch blocks are executed before the stack is unwond, providing the ability for a handler to signal back to the context where the error occurred how it should be handled.
It's sad that no other languages implement this and instead simply decide that exceptions are bad.
The code expander has an entry point which allows the caller to be informed about what variables and functions are free in a block of code. The implementation of this entry point works by intercepting warning exceptions about unbound symbols, and accumulating them in in lists, which are then returned to the caller.
This is a useful thing which reduces the need for users to write their own code walker in certain kinds of advanced macros.
This sounds like a general errors handling issue that nor exceptions nor monadic errors types can prevent.
* panic + catch_unwind, but that's special and not meant or used in practice for error handling
Thinking about it in that sense, classical inheritance has always been based on a fundamentally flawed abstract model.
(Admittedly, purely noun-to-noun inheritance does have its place in a well-structured abstract model, in cases like "every Teacher is a Person", but I stand by Rust's decision to omit it... at least in the near term, because developers are too used to overusing/abusing it out of habit.)
"Everything is an expression" is pretty different. You can say ML/Lisp, but those are non-top-10 languages/styles.
> "Everything is an expression" is pretty different.
No, in Ruby, everything is also an expression; this is sometimes unrecognized because some constructs that are cognates to constructs that are statements in other languages are frequently (but inaccurately) referred to as statements in Ruby (the most frequent example I've seen is "case statements").
Also Ruby is a kitchen sink language, so I don't know if we should be using it as the baseline for a language with a small surface area.
So you praise Go for having structures and interfaces, but you criticize Rust for having structures and interfaces because that's "too weird".
> The enum approach to variant records is too weird.
Why? Because of the keyword? Swift uses that keyword too!
> The error handling is too weird.
What would you rather see?
> The half-functional style is too weird.
I can't think of any way that Rust differs in this regard from, say, Ruby. Or JavaScript.
> The widespread use of closures for simple stuff is too weird.
Again, it doesn't differ from Ruby. (Actually, Rust uses closures/blocks quite a bit less than Ruby does, because of the pull-style iterators...)
> There's too much unnecessary originality.
Such as? None of the features you've mentioned are at all original.