22 ms·
"Rust lacks function overloading, templates, inheritance and exceptions," ...sounds good to me!
by bioneuralnet 2y ago
"Rust lacks function overloading, templates, inheritance and exceptions,"
...sounds good to me!
- stackghost 2y agoExceptions are great. There's an argument to be made that one should handle errors where they occur, but that's often not desirable or even possible. If I call into a library and something goes awry I don't want to have to care about the inner workings of that library. Most of the time it's sufficient for me to try/catch that call and if it fails I'll handle it as gracefully as possible at my level.
- kibwen 2y agoThe workflow you're describing is also how it works in languages without resumable exceptions, except that you're forced to acknowledge when a function call is capable of producing an error. Whether you want to ignore the error, handle the error, or propagate the error, it's all the same; you're just required to be explicit about which approach you're taking (as opposed to exceptions, where the the implicit default is to propagate).
- vacuity 2y agoIndeed. While it is painful for the people who know they have a simpler architecture, making errors and other cross-cutting effects explicit is necessary at some point. It's essential complexity that shouldn't be hidden; it should be addressed from the get-go. Although the industry largely has the wrong incentives and discourages robust, comprehensible programs.
- elcritch 2y agoMy belief is that errors should be handled using effect systems. So in the signature but not muddying the actual return types. Useful effect systems allow the end user to decide where and when to have the compiler enforce errors are handled. Nim has had an effect system for a while but became much more useful when `forbids: [IOError]` was added. It makes it easy to ensure certain type of errors are handled at specific points. More languages should embrace effect systems. Ocaml's is even used to implement multithreading support, albeit effect systems vary widely in design and theory.
- consteval 2y agoI mean this is how a lot of exceptions are handled, even in C++. You can use noexcept and whatnot and you don't have to change types and propagate them out. In Rust, you do. Java has maybe the strongest system because not only do you have to declare what can throw but it checks it at compile-time. That's, to me, a full featured effect system. But errors-as-values are all the rage today. But modifying types, especially every type in the chain, is annoying and overly manual IMO.
- elcritch 2y agoYeah, Java had some good ideas but just not quite there on the UX, like many things with Java sigh. Checked exceptions were annoying because you had to manually annotate the exceptions all the way up your chain. The list of effects should be generated by the compiler, and the IDE should show them when desired. Maybe manually annotated at external API boundaries which double as forcing the API dev to handle unlisted exceptions.
- stackghost 2y ago>you're just required to be explicit about which approach you're taking Yes and I'm opposed to such "if err != nil" boilerplate.
- worik 2y ago> Yes and I'm opposed to such "if err != nil" boilerplate. That is not what Rust boilerplate looks like.
- stackghost 2y agoI'm aware. One of the criticisms often leveled against Go is that it's needlessly verbose when handling errors, which is why I chose that example.
- 6equj5 2y agoAnd you're not opposed to try-catch boilerplate?
- stackghost 2y agoBoilerplate is code you have to write almost as a pro forma thing. If (in go lang, to continue my example) you're just going to keep copy pasting the same if statement to return `err` up to some higher caller, then why write all those lines when at the top level a single try/catch can remove potentially dozens of lines of code?
- soulbadguy 2y ago> except that you're forced to acknowledge when a function call is capable of producing an error Acknowledging the error is handling the error even if partially. The point of exceptions is to only acknowledge error that one can/knows how to properly handle
- kibwen 2y ago> The point of exceptions is to only acknowledge error that one can/knows how to properly handle In Rust this takes a single character. There's effectively no cost to having the programmer acknowledge the error, and there's a large benefit in that you now know that there's no such thing as an error that the programmer ought to have handled but was simply unaware of. That's a huge benefit for writing resilient software.
- elcritch 2y agoIt's one character in the best case. In the worse case you need to convert the error types to your error type which just re-wraps or replicates the upstream error type. Then repeat this for every library type you use. Zigs error design seems saner in this aspect at least. Rust, IMHO, just makes errors require lots of unnecessary manual labor instead of being smart about it. Alternatively everything just gets put into a `dyn trait` and you're effectively just bubbling up errors just like with exceptions, but with way more programmer overhead. The performance overhead of constantly doing if/else branches for errors adds up as well in some situations. Of course a fair bit of Rust code just uses `unwrap` to deal with inconvenient errors.
- binary132 2y agoOne thing I’ve wondered about is, isn’t the cost of checking for the failure case in the good case all the time actually worse (even if only slightly) than the cost of not throwing, which is nothing?
- vacuity 2y agoThere's indeed a predictably present cost for checking for failure all the time. Exceptions, depending on the implementation, often do come with runtime overhead too. If the determining factor is a slight performance gain of exceptions over ubiquitous checking, that would be an exceptional (ha) case. I daresay there are almost always other more salient factors, if harder to rearchitect around.
- binary132 2y agoAIUI most implementations of exceptions only carry a cost on unwind, but I'm not the expert here.
- otabdeveloper4 2y ago> you're forced to acknowledge when a function call is capable of producing an error All functions are capable of producing an unbounded set of errors. (Yes, programming is hard.)
- 112233 2y agoExceptions in C++ are the closest we have to the implementation of the COME FROM proposed in "A Linguistic Contribution to GOTO-less programming". It takes statically typed C++ and turns it into dynamically typed language. Throwspec is dead, anything can throw, except when noexcept, then nothing can throw. It is next to impossible to reason about control flow. Dynamic linking opens whole new dimension of this wormcan. Do you handle exceptions in your constructors? How about constructors of your function arguments? Not to mention the non-trivial cost in code size, that makes exceptions a non-option for embedded use.
- jacobp100 2y agoNoexcept is the biggest scam. It basically wraps every call to a noexcept function in a try/catch, and calls terminate in the catch. Actively harmful for performance
- consteval 2y agoMy understanding of exceptions implementation in C++ is that there's zero performance cost if an exception isn't thrown. Try... catch isn't implemented as a branch, I believe.
- 112233 2y agoThere indeed is zero perfomance cost. Code size cost, however, can be astonishing.
- favorited 2y ago[[noexcept]] was never intended to be a standalone performance boost for arbitrary functions – it was proposed & accepted so container types could make the "strong guarantee" (of the so-called Abrahams guarantees): operations can fail, but failed operations have no side-effects. It allows, for example, std::vector's resize operation to move its contents rather than copying them, iff its element's move constructor is [[noexcept]]. If the move constructor could throw, the items must be copied so the original buffer is unchanged until the entire copy transaction is complete. std::move_if_noexcept <https://en.cppreference.com/w/cpp/utility/move_if_noexcept https://en.cppreference.com/w/cpp/utility/move_if_noexcept> "N3050: Allowing Move Constructors to Throw (Rev. 1)" (2010) <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3050.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n30...> "P2861R0: The Lakos Rule – Narrow Contracts and noexcept Are Inherently Incompatible" (2023) <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2861r0.pdf https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p28...> "P2946R1: A Flexible Solution to the Problems of noexcept" (2024) <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2946r1.pdf https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p29...>
- Sytten 2y agoRust has problems but certainly not those for sure!
- jampekka 2y agoRust unwraps many such problems.
- _aavaa_ 2y agoWon't somebody please think of the children?
- bfrog 2y agoThose are all features of rust not bugs. Function overloads are evil evil evil. Requiring some mental gymnastics by the reader to pretend to be the compiler what function is actually called. That sucks.
- nneonneo 2y agoRust does support a form of overloading via custom traits. It’s true that you can’t overload e.g. different numbers of arguments (and the lack of default/keyword arguments is especially annoying here), but you can overload a function by having it be generic over a trait argument and then implementing that trait for each overloaded type.
- tialaramex 2y agoI would argue that although this is mechanically equivalent it encourages a much healthier design approach. Take Pattern. One way to look at Pattern is to say that this way we can provide the ad hoc polymorphism of overloading, for functions like str::contains or str::split or str::trim_end_matches -- but as a trait we can see that actually Pattern has discernible semantic properties, clearly a compiled regular expression could be a Pattern for example (and with some feature flags that's exactly correct) In contrast in C++ there are often functions which use/ abuse overloading to deliver separate features in the same interface, expecting that you'll carefully read the documentation and use the correct feature by passing the right type and number of parameters. Constructors are the worst for this, Rust's Vec::with_capacity gets you a growable array with a certain capacity already allocated ready for use -- C++ does not have such a thing - you must make a std::vector and then separately reserve enough space, but it looks like it might have this feature in its constructor as an overload because the constructor has an overload which is the right shape - however that's actually a very different feature, it will fill the std::vector with default initialized objects, rather than reserving capacity for such objects.
- bfrog 2y agoThis is generics. Calling x.some_func() in rust means there is either a type specific function or an impl trait. If more than one option is there rust requires more explicitly calling the types function with x as a parameter. E.g. Something::some_func(x) I’ve never read rust code where I’m entirely guessing which overloaded function is being called. C++ has an entire set of overload resolution rules around this! https://en.cppreference.com/w/cpp/language/overload_resolution https://en.cppreference.com/w/cpp/language/overload_resoluti...
- blastonico 2y agoA proper OO support makes difference for some use-cases. That Serenity OS guy is building a web browser and recently spoke about it. Game developers also complain about the lack if it in Rust.
- vacuity 2y agoAs is typical in Rust-land, lots of talk but implmentations (let alone remotely complete ones) are harder to come by. Partly a procedural and social issue, not just technical queries, which is disappointing. At least delegation (IIUC closer to concatenation as defined in [1]) is currently being implemented, but I can't say how much weight it can actually carry for the OOP efforts. [1] "On the Notion of Inheritance": https://users.csc.calpoly.edu/~gfisher/classes/530/handouts/readings/papers/type-systems/p438-taivalsaari.pdf https://users.csc.calpoly.edu/~gfisher/classes/530/handouts/...
- saghm 2y agoI had the same reaction when reading that part, but it's worth noting that the article used this quote in the context of migration from C++ codebases to Rust, not as a critique of Rust in a vacuum. The next part of the quote clarifies this: > These discrepancies are responsible for an impedance mismatch when interfacing the two languages. Most code generators for inter-language bindings aren’t able to represent features of one language in terms of the features of another.
- kjrfghslkdjfl 2y ago[dead]