Why I Prefer Exceptions To Errors

  Рет қаралды 61,450

ThePrimeTime

ThePrimeTime

Күн бұрын

Recorded live on twitch, GET IN
Article
cedardb.com/bl...
By: Philipp Fent | / philippfent
My Stream
/ theprimeagen
Best Way To Support Me
Become a backend engineer. Its my favorite site
boot.dev/?prom...
This is also the best way to support me is to support yourself becoming a better backend engineer.
MY MAIN YT CHANNEL: Has well edited engineering videos
/ theprimeagen
Discord
/ discord
Have something for me to read or react to?: / theprimeagen
Kinesis Advantage 360: bit.ly/Prime-K...
Get production ready SQLite with Turso: turso.tech/dee...

Пікірлер: 805
@JohnDoe-bu3qp
@JohnDoe-bu3qp 13 сағат бұрын
From a Java perspective, my impression is that Prime is more against unchecked exceptions than checked exceptions.
@slr150
@slr150 13 сағат бұрын
Exactly my thought. In addition to this checked exceptions can be passed on to the callers and the final handler will have available to it the full stack trace, however when you pass on error values to the caller information about the failure site (stack trace) is lost.
@s3rit661
@s3rit661 13 сағат бұрын
@@slr150 I don't know why he uses JS as example of Try Catch and not Java, throws in method signature is a thing
@awesomedavid2012
@awesomedavid2012 13 сағат бұрын
The thing is, people complain about errors as values because of the "if err != nil" boiler plate, but if you check all of your exceptions the same way, you get massive try catch boilerplate
@lppedd
@lppedd 13 сағат бұрын
@@slr150 That's actually the feature of exceptions I like the most. Being able to trace the ENTIRE error chain. The point is Java devs have been extremely inconsiderate in the last couple of decades when using exceptions (skill issue), and have created a monster out of them.
@s3rit661
@s3rit661 13 сағат бұрын
@@awesomedavid2012 I'm tired of people complaining on boilerplate code, that is there for a reason, you want control? You have to write more code, that's just how it works, Java gives you that type of control into an higher level envirorment and everyone hates it for that, they should learn how to categorize stuff and put them into the right place and boilerplate will be less a problem
@alexpyattaev
@alexpyattaev 13 сағат бұрын
Bro has never used rust web frameworks. They all return 500 code when a handler panics. Exactly as a top-level exception handler would. Effectively, unwrap is throw for most web frameworks in rust.
@n30v4
@n30v4 8 сағат бұрын
and its not realy hard to implement when you need it...
@getattrs
@getattrs 8 сағат бұрын
So what you're saying is they have shitty exception handling ?
@alexpyattaev
@alexpyattaev 8 сағат бұрын
@@getattrs no, they have the normal rust Result based handling, but if a handler calls unwrap() and panics, then there is a default handler for all panics that does a sensible thing and keeps the server itself running. Of course, you can customize it as you see fit, same as in C++. When people say rust does not have exceptions, they just do not know how panic and unwind works and thus assume they are not supported. And in reality they are just named different and tucked away so devs do not abuse them for everything as in c++.
@declspecl
@declspecl 6 сағат бұрын
@@getattrs That's what literally every single web service framework already does lol. Spring Boot 4xx/5xx status codes are literally just exceptions
@isodoubIet
@isodoubIet 5 сағат бұрын
@@alexpyattaev 1. Rust panics absolutely are shitty C++ exceptions. 2. C++ devs don't "abuse" exceptions, they _use_ them, as they should because it's the superior error handling strategy for most situations. 3. saying panic is "supported" is a silly argument in favor of the rust model because rust developers will generally design their code around the errors as values models and therefore drop a bunch of spaghetti at the first sight of a panic. Having exceptions theoretically supported and having them as well-integrated, idiomatic parts of the language are vastly different things.
@allesarfint
@allesarfint 8 сағат бұрын
This article would have been less upsetting if it was called "Why exceptions aren't as bad as you think"
@yellingintothewind
@yellingintothewind 7 сағат бұрын
Calculating fibonacci recursively is a common stand in for some moderately complex task that will reach very high stack depths. You could substitute in parsing XML tags, or any other tree data structure, and the point would stand. If you use errors as values, then your error checking code is included in-line with the rest of your code, which means an if check and a short jump. This means your hot path code is bigger and fits in lower level caches less well. Exceptions use a long jump table stored externally to the local hot code. So all of the exceptional path code is _not_ sitting in your local caches. This makes the non-exceptional case run significantly faster, at the cost of making exceptional code marginally slower. When you throw an exception, that throw statement _is_ a long jump, to the global exception dispatcher, which takes the current program counter as an index into the exceptional path table to determine where to go next. The where-to-go-next will be the stack cleanup for each frame between the current continuation and wherever your first catch is. Again, this is out of band to your regular code, so will need to be loaded into the L1 cache when an exception happens.
@Tony-dp1rl
@Tony-dp1rl 2 сағат бұрын
Um ... exceptions don't happen often, so that is a bit of a silly way to look at it. You want your no-exception case to be fast, not your exception case.
@AlecThilenius
@AlecThilenius Сағат бұрын
And what prevents the Rust compiler from doing that exact optimization when errors are hoisted with the `?` operator? Aka the "performance gain" is an implementation detail, not a fundamental property of error handling styles. Also who uses recursion in production server code? That's a terrible idea.
@xFlRSTx
@xFlRSTx 9 сағат бұрын
the main trick to recover from OOM is to always have the memory pre allocated for handling the error, and/or(usually and) having some of your used memory being forced reclaimable, like if it was minecraft you could have pre-allocated memory for everything u need to drop chunks (sections of game world) from memory.
@thewhitefalcon8539
@thewhitefalcon8539 7 сағат бұрын
Minecraft used to have a useless 2 megabyte array. On OOM, it would set the array reference to null before showing the fatal error screen
@lever1209
@lever1209 3 сағат бұрын
most of the time whenever Minecraft runs out of memory it just crashes, it only recovers from out of memory in very specific locations
@davidlofstrand
@davidlofstrand 13 сағат бұрын
Error handling in complex systems is highly contextual. You might not have use a single silver bullet that kills all problems at hand. For example when you write SC software with high performance requirements you tend to prefer return error by value, while errors in a UI exceptions are usually preferred.
@---cz7vs
@---cz7vs 14 сағат бұрын
Early bait: FP is the best paradigm because in order to get a fully initialized type, you must handle any errors along the way.
@adambickford8720
@adambickford8720 13 сағат бұрын
Not really: var nullOrValue = Try.of(() -> "foo") .mapTry(this::someExceptionThrowingMethod) .mapTry(this::randomNPEMethod) .getOrElseGet(null);
@Daniel_Zhu_a6f
@Daniel_Zhu_a6f 13 сағат бұрын
what is a fully initialized type?
@monkemode8128
@monkemode8128 13 сағат бұрын
​​​​@@Daniel_Zhu_a6fYes I'm new to FP. I think it's referring to immutability, like, if you're building out a data structure or value via multiple functions, if one fails, your code fails. Whereas with objects which have multiple methods to get them to the state you want, it can be more difficult to trace out and maintain "good" state? But I feel like both paradigms have ways to solve that, so I'm probably wrong. I figure if I post the wrong answer someone will come around and correct me. 😅
@---cz7vs
@---cz7vs 12 сағат бұрын
@@Daniel_Zhu_a6f a good example is a file handle. With a Maybe monad (FP) you must handle the file not opening. In procedural (fopen) you usually have to remember a null check. OO approaches usually throw an exception in my experience.
@tyyrrr
@tyyrrr 12 сағат бұрын
Haskell has exceptions in their "spec"
@pashadia
@pashadia 13 сағат бұрын
TLDR; somebody thinks that throwing exceptions is better that `unwrap()`-ing `Error`s, because he doesn't know what else to could do with them. They're not wrong.
@ivanjermakov
@ivanjermakov 11 сағат бұрын
They're wrong because if you don't know what to do with errors, you return them upstream. As Prime described, unwrap() is for asserting program correctness (i.e. impossible state) rather than ignoring errors.
@defeqel6537
@defeqel6537 11 сағат бұрын
@@ivanjermakov that's why I don't like ? -operator, makes it too easy to move the error to someone else
@yyny0
@yyny0 10 сағат бұрын
I don't think that's really the author's point, more-so that throwing exceptions is better than re-raising errors. Which it is, re-raising creates branches which as explained in the post can trash performance in a number of ways.
@FrancoGasperino
@FrancoGasperino 9 сағат бұрын
​@@defeqel6537 It is someone elses problem, because it's often contextual on the caller. For example: function save-my-stuff-to-db(stuff) { } function write-important-data-to-db(important) { save-my-stuff-to-db(stuff) } function write-transient-garbage-record-to-db(garbage) { save-my-stuff-to-db(garbage) } If returns an error value, do you believe that handling it as a value within that function is behaviorally correct given the two different caller contexts?
@defeqel6537
@defeqel6537 9 сағат бұрын
@@FrancoGasperino it may or may not be someone else's problem, but ? is easy to abuse and lose context in your example, what if you are saving stuff to 2 different DBs? And you get an error message from one of them, but fail to pass that information to the caller, and instead just ? the error forward which is the same error for both DBs
@alienm00sehunter
@alienm00sehunter 12 сағат бұрын
In rust you can recover from a panic. Most server libraries do this on every connection.
@thingsiplay
@thingsiplay 13 сағат бұрын
I prefer languages where I know what errors the function can have. If a function is updated with a different Exception, then the caller can't know this. Program will happily run, even if the Exception is not handled. But if the error type was coded into the function signature, then the caller only needs to know about this function and not the entire stack. And any change would make it incompatible, and force the caller to update to handle any error.
@thewhitefalcon8539
@thewhitefalcon8539 7 сағат бұрын
That's none of them not even error languages
@thingsiplay
@thingsiplay 7 сағат бұрын
@@thewhitefalcon8539 What do you mean?
@Quantris
@Quantris 4 сағат бұрын
you seem to be describing checked exceptions in Java
@AnyVideo999
@AnyVideo999 11 сағат бұрын
The comments and Prime both seem to have missed the key point in the handling section: Thrown exceptions _force_ the error to include context by creating a stack trace etc. By having the language handle exceptions, you can enforce certain rules about your errors that you can't when errors are merely values and then you are at the mercy of the individual writer.
@diadetediotedio6918
@diadetediotedio6918 8 сағат бұрын
1. The stacktrace is not always necessary, literally, when you describe your errors as strong types modeled in your application. LITERALLY, they are not needed. And MANY times the stack traces are devoid of any meaning (for example, when you are dealing with an event loop). 2. This is not something exclusive for exceptions, you can pretty much have stack traces in errors as values as a default for a language (and you can do it in many languages with libs), so it do not make to say this is a point in defense of exceptions.
@squishy-tomato
@squishy-tomato 2 сағат бұрын
@@diadetediotedio6918 the big selling point of stack traces is context. The point that "you can have stack traces in errors as values" is pretty much useless because that is NOT the default behavior, so you have to rely on every point of the program that may err to add stack trace info, or run it in a controlled env with e.g. RUST_BACKTRACE tuned on. Finally, in the cases where stacks are less useful (as you mentioned event loops), errors as values don't help either.
@AlecThilenius
@AlecThilenius Сағат бұрын
I can maybe kind-of buy this one, it's the best argument I've read so far, but it also feels contrived. You need stack traces only when a human needs to look at the error. If the lib you're using returns crappy errors (without proper context) then yeah, you're somewhat out of luck. But... if it throws crappy exceptions to stack frames that mean nothing to you, you're likely just as out of luck. Aka the lib author wrote bad error handling. If it's your own code, then you can trivially get traces on every single result with `anyhow` Result type.
@19975amitsingh
@19975amitsingh 12 сағат бұрын
If I'm correct, "std::expected" is slow because the copy constructor of the "std::string" is being invoked every time you construct the "std::unexpected". To reduce memory allocation, the author should've used the move constructor by using "std::move". Therefore, the latter implementation allocates string on the heap multiple times compared to the exception implementation. Maybe the compiler is unable to optimise the allocation or the pattern because the "std::expected" is a new feature, but I think it'll improve in the future as more use cases are optimised.
@geek2145
@geek2145 11 сағат бұрын
Yeah this code is really weird. I don't know why they reconstruct the unexpected value, when they could just return the local variable so it's move constructed. I actually misread the code at first because I have no idea why anyone would do it like this...
@geek2145
@geek2145 11 сағат бұрын
Also, c++23 now has support for monadic operations to std::optional, so a more fair comparison would have used that instead.
@19975amitsingh
@19975amitsingh 11 сағат бұрын
@@geek2145 I just looked into it quickly inside Godbolt to confirm what I said, and I'm not saying something wrong. I saw calls to the "new" function that shows it's calling to the copy constructor. I used clang c++23 with O3 flag.
@defeqel6537
@defeqel6537 11 сағат бұрын
@@geek2145 optional is not an error, nor does it contain error context (edit: but I totally agree that there is no reason to make copies) (edit 2: actually unless you can get copy elision going here, the string will always be copied due to SSO, which is a stack operation, but still not free)
@19975amitsingh
@19975amitsingh 11 сағат бұрын
@@geek2145 The article was talking about exception allows you to pass more context to the catcher using strings. So, I doubt he'd use optional since it only allows two states; either you have the value or not. It doesn't allow you to return any other value or context using value.
@MartialBoniou
@MartialBoniou 12 сағат бұрын
No word about Common Lisp-like condition system? Ignorance is bliss, I guess.
@mettemafiamutter5384
@mettemafiamutter5384 6 сағат бұрын
Recently at my job I was parsing a binary file containing data structured to a given schema and encoded to binary with a known algorithm. The program was written in Ruby. When I was done with the logic it took me less than 10 minutes to wrap my key-value parsing loop in a try-catch block, catching any exception and skipping the offending k-v pair (while informing the user of it). The result was that all of my parsing code was written naively and any null pointers, array out of bounds, division by 0 etc. as a result of invalid input was handled without me having to think through all of the failure modes and all of the places it could go wrong. Even with a type system to inform me of those places, it would have taken hours if not days to do what took me 10 minutes. I work in embedded and most days I write C 8 hours but I was very glad I didn't have to do that specific job in C because it was for a host tool. I think being able to handle such errors through exceptions is valuable and valid.
@alexlowe2054
@alexlowe2054 3 сағат бұрын
This. Exceptions allow you to only care about the happy path. Sometimes, you really just want a goto statement to jump higher up the call stack where you can do something useful about the error. Needing to check errors on every function before using the result is nothing but worthless boilerplate when you already know you can't do anything about that error and you have to pass that up the call stack. Yes, unwrap exists, but in that use case, it's just worse than using exceptions. Unwrapping errors and providing type information on those errors means you're re-implementing one of the most annoying parts of async programming because you now have blue/green functions again, where you can't call a "green" function that returns an error without turning your "blue" non-error function into a green function. It's the worst parts of Java checked exceptions for an entirely new generation.
@rosehogenson1398
@rosehogenson1398 7 сағат бұрын
Are we acting like the OOM killer isn't a thing? Unless you're writing for kernel or embedded, recovering from OOM is impossible since the kernel OOM killer will just terminate the process
@keithjohnson6510
@keithjohnson6510 2 сағат бұрын
I think your getting OOM as in Out of Memory and the OOM killer as been the same thing. Its totally possible to recover from an OOM at a program level, as you can set the process to have a maximum amount of memory that kicks in, so the OOM killer will never get that far, as the system will still have plenty of memory. Also you can turn of the OOM killer, and is something I do with Arch, rather just have a large swap and monitor resources, randomly killing processes just seems bonkers having the OS reboot is much safer.
@DjRio0001
@DjRio0001 10 сағат бұрын
Prime still doesn’t understand what Hawktua is. He’s saying he’s enthusiastically servicing that article!
@ElvenSpellmaker
@ElvenSpellmaker 13 сағат бұрын
_"Why aren't you becoming bigger, that is way too tiny, way too tiny"_
@Peregringlk
@Peregringlk 12 сағат бұрын
About 13:25, you follow the RAII style. Don't ever manage resources manually. Wrap them in an object that releases the resource in the destructor, like unique_ptr. That knowledge is implicit because the writter is probably a C++-programmer that is so much used to think in terms of RAII that he forgot that using dynamic memory manually was even a possibility.
@geek2145
@geek2145 11 сағат бұрын
Imagine calling a raw new instead of std::make_unique. I could never.
@yyny0
@yyny0 10 сағат бұрын
I don't like the unwind argument that much, because a `return` _also_ unwinds the stack, in both Rust and C++. `throw` _does_ unwind the stack more efficiently than `return` does, though, which I think is the argument that the author is _trying_ to make.
@TheSulross
@TheSulross 10 сағат бұрын
In C++ I use a template class called defer_action which accepts a lambda - write a cleanup lambda to clean up any arbitrary resource (or back-out actions like “unregister”) and then supply to the constructor of this class. It will do RAII for that arbitrary resource. It also has a disable method so the action can be avoided on function exiting, and a method to explicitly invoke the action and then put it into disabled state. These two methods are occasionally useful for when the cleanup will no longer be necessary, or if for some reason there’s a point in logic flow where need to explicitly invoke the cleanup (instead of allowing to be deferred to outer function exiting time). This template class is hyper useful, gets utilized all the time, and it’s very puzzling to fathom why the C++ std template library doesn’t have equivalent of such. It should have been there since lambdas were added in C++11, but yet it doesn’t exist. However is super easy template class to write. Yet because RAII is a centerpiece of C++ goodness there should be standardized practice around such a facility so that new C++ developers know to use it and how to use it. Even decent books on learning modern C++ don’t bother to illustrate such a template class and educate readers to such - is weird that these authors miss out on aiding newbies with something so generally useful and very often needed. What programmer doesn’t deal with resources other than memory allocations? And it’s extremely tedious to write a custom RAII class for every occasion this comes up. And it’s kind of lousy to have to hack std::unique via custom delete functions to deal with these cases. The smart pointer template classes were aimed squarely at RAII management of memory objects. Guess should try my hand at writing a proposal of adding this to the standard as no one else seems to have done so (am I wrong about that?)
@LeeLikesFrenchFries
@LeeLikesFrenchFries 8 сағат бұрын
​@@TheSulrossIt sounds like you're describing a scope guard. lots of similar implementations exist. and I agree, something like a generic resource management object, (even a "before/after" paradigm type of class) would be useful in the standard library
@diadetediotedio6918
@diadetediotedio6918 8 сағат бұрын
@@yyny0 " `throw` does unwind the stack more efficiently than `return` does" Why exactly?
@peculiar-coding-endeavours
@peculiar-coding-endeavours 6 сағат бұрын
The main thing I get from this article: if you didn't go further than a 1h tutorial that you skimmed through, don't write a whole article to take down a certain concept.
@jenreiss3107
@jenreiss3107 9 сағат бұрын
exceptions are fine, as long as you have some way to manage them with the type system, and they cannot be left unhandled on main scope. There should be some type `Except` with functions `throw(err: E) -> Except` and `catch(action: Except, handler: Fn(E))` that can be eliminated with something like `runExcept(action: Except) -> Result`. Oh wait, that's the `Exception` Monad in Haskell
@squishy-tomato
@squishy-tomato 2 сағат бұрын
Unhandled exceptions are absolutely fine, as long as the program terminates after it instead of being allowed to enter an undefined state. Not every error is possible to solve and not every problem is worth solving.
@lapissea1190
@lapissea1190 11 сағат бұрын
This whole conversation seems a bit heated and does not get down to the core. Exceptions and error values are the same thing IF you are talking about checked exceptions. Unchecked ones are what Prime has trauma with. I am working with some very performance sensitive code and I would NEVER use errors as values as it would literally half the performance. I think it is very reasonable to have a separate unhappy path when the unhappy case is a rare valid state or a coding error.
@Jmcgee1125
@Jmcgee1125 10 сағат бұрын
Error values are much more strict on "you must handle this" while exceptions often allow you to defer. That's why all good exception-based languages should force you to declare what exceptions your function might throw, propagating them to parents. Looking at you, Python... More boilerplate? That's punishment for not handling error cases!
@bcfuerst
@bcfuerst 13 сағат бұрын
This sounds more like a rant against unchecked exceptions. Checked exceptions force you to deal with the error at the appropriate place and you have full control over where that is.
@awesomedavid2012
@awesomedavid2012 12 сағат бұрын
Yeah but the syntax of try catch is worse than if err != nil. What if you neglect to catch a specific kind of error? Was it intentional?
@stysner4580
@stysner4580 12 сағат бұрын
The point still stands that you don't know something can throw. At least with error as value you can choose whether to let the caller know if they need to handle a potential error, and for the "little corner" argument in the article: that still works. Just handle the error where you are.
@SussyBaka-nx4ge
@SussyBaka-nx4ge 12 сағат бұрын
@@stysner4580 with checked exceptions in Java the exception type is in the method signature and you're forced to handle it or rethrow it, kinda similar to in rust if you want the value you need to unwrap, though the safe access operator is a very nice convenience in rust. The problem is unchecked exceptions exist, and worse, people use them as a kludge to avoid changing method signatures or to pass exceptions through lambdas. Arguably part of the problem is that it's even allowed to catch unchecked exceptions.
@diadetediotedio6918
@diadetediotedio6918 11 сағат бұрын
And you just need to use like one of the 2 unique mainstream languages that adopted that, one of them is not entirely happy with it and the other had its spiritual successor (Kotlin) ditching them entirely. I think checked exceptions solve the problem, but they are much more annoying to deal with.
@stysner4580
@stysner4580 11 сағат бұрын
@@SussyBaka-nx4ge I agree that a "half baked" solution or just providing more and more options isn't going to fix the problem at all. Errors as values immediately forces developers to make a choice, including passing through errors to higher functions to let the caller know that something is fallible, or not if it's not needed. Something in the function signature letting you know something is fallible is the most important.
@todo9633
@todo9633 3 сағат бұрын
If I've learned anything about programming, it's that everyone has their preferred way to do things, and that preference is heretical to 90% of other programmers.
@MrAlanCristhian
@MrAlanCristhian 8 минут бұрын
Here is another lesson: there are a lot of complainers on the internet. None of the drawbacks of both approaches are important enough to make any difference.
@melodicmonster
@melodicmonster 10 сағат бұрын
I am obsessed with the Fibonacci sequence, and C++ was my first love. Seeing a runtime, exception-laced implementation instead of a const lookup array generated at compile-time hurt me deeply. LOL.
@wolfgangrohringer820
@wolfgangrohringer820 6 сағат бұрын
I am always so happy when a Starcraft reference is dropped. APM driven development, let's go!
@SimGunther
@SimGunther 14 сағат бұрын
Exceptions would be storage device/peripheral failures, solar flares, comets hitting the planet, or crossing the Bermuda Triangle/any place with high strangeness such as haunted mansions orSkinwalker Ranch. The last thing you'd expect to be an "exception" would be file read/malloc errors as they're just things you need to anticipate or else you're not a "real software engineer".
@youtubeenjoyer1743
@youtubeenjoyer1743 13 сағат бұрын
@@SimGunther This is a common misconception. The word exception is a poor choice, because there are no exceptional or unexpected situations in programming. It should have been called “Error” instead, because that’s what it is.
@Jabberwockybird
@Jabberwockybird 13 сағат бұрын
So this is kind of the opposite of python's mindset of using exceptions for controlling flow? I'm surprised I don't see a lot of python devs jumping in to these errors as values videos
@awesomedavid2012
@awesomedavid2012 13 сағат бұрын
​@@youtubeenjoyer1743if the circumstances aren't exceptional, then why do they justify being thrown? Why not just return a value? Some functions DO just return a value, like a -1 returned by indexOf when there is no index. That isn't exceptional so it doesn't warrant interrupting control flow
@youtubeenjoyer1743
@youtubeenjoyer1743 12 сағат бұрын
@@awesomedavid2012 what do you mean by “interrupting control flow”? Error handling looks pretty much the same in complex applications, it’s a pattern, if you will. This language feature attempts to make error handling more convenient.
@CottidaeSEA
@CottidaeSEA 10 сағат бұрын
@@awesomedavid2012 Sentinel values only work in some circumstances and are generally just awful to work with, since they require you to check what sentinel value a specific function uses. It's something you learn over time for sure, but if working with a brand new library, what then? Also, assume -1 is valid, what do you return instead? Another scenario, assume something changes in the library and the previous sentinel value ends up becoming a valid value. Now you've got to track down every single case where that's a possibility and change it. Obviously that's the case if you throw a new exception as well, but you'd hopefully be notified with errors in your IDE or compiler if that's the case. So tooling is a major benefit when it comes to exceptions compared to sentinel values and there are better solutions to sentinel values regardless, such as the Option monad. Option either having Some or None is far superior to a sentinel value as it immediately tells you what you're working with in the type definitions. Because as you say, it's not exactly an exception or error that the value you're looking for is not present in the array/collection, so semantically it makes sense to not throw in such a case. I think it makes more sense to change the return type rather than using a sentinel value however. Another alternative is to have a more complex object. Example: Collection.find(E item) Returns contextual information such as isPresent(), indexOf(), replace(E item), remove() and similar. That way you're dealing with something you can directly interact with in a more natural way (from a non-programmer perspective) and also do everything with built-in functionality. The only argument I have in favor of sentinel values is performance in low-level code. Integrated systems and whatever else. That way it's easier to reuse memory and ensure you're not blowing up the RAM usage for no good reason. In any other situation, I think just having a different return type is more reasonable.
@ferdynandkiepski5026
@ferdynandkiepski5026 11 сағат бұрын
A mixed approach is correct. Exceptions in C++ aren't meant to be called over and over. In fact they are slow if they are hit. If you know something will be failing relatively often, you should use errors as values instead.
@retropaganda8442
@retropaganda8442 11 сағат бұрын
Unwinding is not for printing the stack, it's for calling object destructors to release ressources, like unlocking, closing system handles, freeing the heap etc. All of that automatically!
@ilovepickles7427
@ilovepickles7427 5 сағат бұрын
Swift has full support for retuning an error object with whatever you want in it, and even now has typed throws that tell you the type of the error you can expect. Bad exceptions = bad language implementation.
@CottidaeSEA
@CottidaeSEA 10 сағат бұрын
What Prime is complaining about is really just a language allowing you to basically forget that exceptions are thrown. That's poor language design and is not a problem with exceptions themselves. Java requires exceptions to be caught or thrown and that's required on a syntax level to even compile. It's one of the good things about Java. Now, if someone decides to throw a generic Exception all the way to the top of your application and you can no longer see where something is thrown or where something is coming from, then that's just a pure skill issue. I do wish more languages were better at telling the developers that a function can throw though. Errors as values enforces this, which is a good thing. I just don't believe it's a problem with errors as values vs. exceptions.
@FraggleH
@FraggleH 12 сағат бұрын
I'd be interested in a deeper discussion on Zig's decision to forgo error structures. The article's point seems to be that error context is super important, and Prime's rebuttal is that exceptions aren't a prerequisite for error context, but it seems that Zig explicitly abandons error context and are proud of that decision. I'm sure I'm missing something fundamental, but I don't know what it is.
@diadetediotedio6918
@diadetediotedio6918 11 сағат бұрын
The first thing you should ask is: What does zig have to do with prime's arguments? Is Zig the unique language that deals with errors without exceptions?
@madlep
@madlep 12 сағат бұрын
Why not both? Errors as values are great if you foresaw that particular error happening; know what to do about it; and are actually able to at that point - "the unhappy path". But you can't always do that. You want that operation to crash, and have some higher level thing in the system put the system back into a known good state. This is what Erlang/Elixir do, and the approach is a big part of writing resilient systems on the BEAM VM.
@diadetediotedio6918
@diadetediotedio6918 11 сағат бұрын
I like the mixed approach, my problem is more when the language leads you into dealing with your errors as exceptions first instead of representing them as simple errors (like JS, Java or C#).
@Zuftware
@Zuftware 11 сағат бұрын
You can't have both. One or the other will became idiomatic, every library will use it and you'll have to use it to. Or you can get c++ situation when there is nothing working together because each library use different string, different threads, different exceptions etc.
@diadetediotedio6918
@diadetediotedio6918 11 сағат бұрын
@@Zuftware You can absolutely have both if your language is structured for it, exceptions were made to be , people made them the default in languages that have them because options were not provided for the other case.
@dearlordylord
@dearlordylord 11 сағат бұрын
Both is the way! Unhappy path has a lot of "flavours"; it's rather a spectrum really. Assertions/sanity checks that the "state of the world" in the program still realistic - yes please, blow up an exception, there's nothing to do anymore. Anything else - it depends, it's "on the spectrum"; most likely, error values is the way to go.
@xbmarx
@xbmarx 10 сағат бұрын
Joe Armstrong was low-key right about everything. Erlang does train you to expect exceptions or things to blow up absolutely anywhere.
@MrSonny6155
@MrSonny6155 10 сағат бұрын
Speaking as a filthy casual who barely programs nor uses a _real_ programming language, exceptions are great for just getting things done without having to mind every single failure case across every level, then refactor later. Having exceptions built-in to your scripting language to drop a stack trace is often just good enough. And if I can't catch an exception or am able to lose data due to failures, it's almost always fixable by just rearranging my code to be slightly less stupid. But obviously, this is not the perspective of someone building any sizable app, real-time video streaming software, nor a robust telecomms system. I'm perfectly fine with my 3-day joke project imploding from an uncaught recursion depth skill issue.
@aswingangadharan6986
@aswingangadharan6986 12 сағат бұрын
For me error as value and exceptions are kinda same, I prefer error as value because its better syntax
@yyny0
@yyny0 5 сағат бұрын
Agreed, but exceptions have better semantics.
@Karurosagu
@Karurosagu 3 сағат бұрын
Errors as values are simple to read and simple to write
@ANONAAAAAAAAA
@ANONAAAAAAAAA 11 сағат бұрын
Exceptions are the best way to handle error when developing stateless backend applications. All you have to do when an unexpected thing happens is: throw an error, rollback database transaction, report the error to monitoring systems and return 500 response with some sorry message. You can easily implement this kind of universal error handling mechanism with exceptions or try-catch.
@jordixboy
@jordixboy 9 сағат бұрын
can do the same without exceptions. Plus exceptions are way more costly in terms of resources.
@vinterskugge907
@vinterskugge907 9 сағат бұрын
​@@jordixboyYou can't do the same - the result might be the same, but much more code is required. The point is that with exceptions, you only need to consider errors in *one* location. Resource usage is not much of a concern, as exceptions are used for exceptional situations that do not occur often.
@jordixboy
@jordixboy 9 сағат бұрын
@@vinterskugge907 most apps nowadays use exceptions as default error handling, so exceptions everywhere - they are not treated as exceptions
@diadetediotedio6918
@diadetediotedio6918 8 сағат бұрын
@@vinterskugge907 "much more code is required" and? Your point is seriously that all error handling should be made to the point of one single catch location?
@vinterskugge907
@vinterskugge907 8 сағат бұрын
@@diadetediotedio6918 Remember from the original post: "when developing stateless backend applications". I am not saying that *all* error handling should be like this. Rather that, for some types of systems, exceptions are superior to error types because you never need to think about errors except in *one* specific section of the code. The same applies to batch jobs, which I also happen to work with.
@Peregringlk
@Peregringlk 12 сағат бұрын
To achieve exception-safety, the easiest way, theoretically, is to call all functions that could potentially throw an exception first, and then you modify your state after accumulating all the results. That way you don't need to write try-catchs all the way down to revert the state at every possible failure point because you didn't modify any state in the first place. That approach can't be applied in all kind of contexts, but can be done in multitude of them, removing the try-catch hell in the 90% of places. Unless the context of your problem makes that approach unfeasable, the exception-style becomes more readable in general.
@amotriuc
@amotriuc 12 сағат бұрын
agree, handling a state with the exception is not really a big issue as he does claim.
@defeqel6537
@defeqel6537 10 сағат бұрын
that's assuming you know which functions throw
@TheSulross
@TheSulross 9 сағат бұрын
Regardless of which manner of error (or exception) handling, gathering up new state and then after successfully doing that, mutating the “official” state, is a generally good approach. Essentially try to make even complex state changes to be done in an atomic-like (or actual atomic) manner instead of in a piecemeal manner - has benefit of making clean back-out easier to accomplish. The advice here stands alone apart from the actual error handling approach. Have a case in current project where there’s a function that produces a context object - there are a lot of steps involved along the way to fully prepare an instance of this object, which any step could plausibly fail. The function prepares an object instance of this state that as a local stack object. At any point of failure and function return, the object’s destructor will do appropriate cleanup. But if get to the end of function with total success, then this stack object is moved into a data structure to where it becomes runtime operative. The move assignment operator is implemented to be noexcept, of course, so this final step will succeed without failure being a possibility. It’s a pattern that is used in these kind of situations - a handful of times in this particular project.
@isodoubIet
@isodoubIet 8 сағат бұрын
@@defeqel6537 The assumption one should make is that everything can fail. This is because in reality everything _can_ fail. If you assume that a function in Rust can't fail just because it returns a regular value instead of a Result, you'll fall flat on your face the next time you get a panic. Or when you get a hardware memory corruption. Or when someone pulls the plug from the wall socket. Computing is an unholy mess and pretending things can't fail will inevitably lead you down the primrose path. The exception model of prepare-and-commit is the only model that can work in this environment. Even if you use errors as values you still have to do it.
@Peregringlk
@Peregringlk 6 сағат бұрын
​@@isodoubIet You have two distinguish between two types of errors, those that corrups the state machine, and those that are recoverable. For example, running out of memory is a corruption of the state machine, not very different from the CPU itself being broken. The metal itself can't obey you. Your required execution context itself is broken. There's nothing you can do about it in general. The errors that have to be interpreted as "corruptions of the state machine", and hence not recoverable, is not worth to even be checked. Of course, the definition of "non-recoverable" is kinda context-sensitive. In an embedded system where your program is the only thing executing you can add lack of memory as a recoverable error and thus handing it. If you are the kernel, same. If you are a user program sharing space with an unknown amount of other programs where the kernel can even kill you all of the sudden if you are consuming too many resources, you don't even check that you run out of memory. You assume that requesting memory will always succeed. The speed at which you can simplify your code when you assume you will always have memory is, incredible. Besides, in my 20ish years programming I have NEVER observed a program of mine getting a null pointer after requesting memory, NEVER. I add here programming errors as well. I never check that some argument was wrong except in development. Having a bug in a program is in my mind equivalent to the execution context itself being broken, and so I remove all of these checks for production. I don't even let the asserts there, I remove it from the source code. Of course, you have to test the source code as possible during development before removing all the check. The best way to make sure the code is right is by having as less code to read and check as possible by storing as much technical details as possible in library abstractions.
@alexsmart2612
@alexsmart2612 2 сағат бұрын
Disappointed by the many bad faith arguments in this video. The point of the fibonacci example was to demonstrate that exceptions are much faster than error handling in a tight loop. Of course it is a somewhat contrived example because nobody calculates fibonacci numbers that way but is it really hard to glean the point being made? You can easily imagine some other function that was doing some computation in a tight loop and you needed to check for e.g. integer overflow. The point is that the version of the code with exceptions would be much faster than the version with error values.
@Kane0123
@Kane0123 14 сағат бұрын
At the outset I’m struggling to see a real world difference if you’re managing exceptions properly… try catching everything is horrendous but if I had to do a check on every returned value is it much different?
@Nootlink
@Nootlink 14 сағат бұрын
The way the error is handled, and with exception, we sometimes don't know which code throws and which code don't.
@wnichols6671
@wnichols6671 13 сағат бұрын
not every value, only error values. ie a result type in rust or error values in golang etc
@gagagero
@gagagero 13 сағат бұрын
Yeah, he complains that the author misuses error values and then misuses exceptions.
@gagagero
@gagagero 13 сағат бұрын
​@@NootlinkHave you ever used Java?
@tedchirvasiu
@tedchirvasiu 13 сағат бұрын
You basically replace catches with ifs. I guess "errors as values" enjoyers find it better because the "try" block causes nesting and visually could get ugly.
@khatdubell
@khatdubell 13 сағат бұрын
The problem with exceptions is that, like goto, people use them inappropriately, because its easy to use. The amount of code i've seen where people throw an exception where simply returning an empty string would suffice is enough to turn me off exceptions.
@amotriuc
@amotriuc 12 сағат бұрын
ex: String To Integer function should it return 0 if string is not a number? It is sufficient, but it is incorrect. Your hate of exception is unfounded.
@defeqel6537
@defeqel6537 12 сағат бұрын
easy error handling often leads to inappropriate error handling, or not designing things such that they cannot fail in the first place
@amotriuc
@amotriuc 11 сағат бұрын
@@defeqel6537 It actually opposite, with exceptions you always have a top handler that will tell you that you had an error and it will be fixed. With error codes if you didn't handle it no one will know and it can stay there for really long time.
@khatdubell
@khatdubell 11 сағат бұрын
@@amotriuc I don't hate exceptions and i didn't say returning an empty string is the correct thing to do in all scenarios. But please, strawman away.
@khatdubell
@khatdubell 11 сағат бұрын
@@amotriuc Also, to more directly answer your question " String To Integer function should it return an empty string if string is not a number?" A string to integer function shouldn't return a string, empty or otherwise, under any circumstances. It should return an integer.
@justgame5508
@justgame5508 10 сағат бұрын
Being forced to handle the errors has a nice physiological benefit, it makes you actively think about errors in your code in a way that a try catch never does
@chaos.corner
@chaos.corner 7 сағат бұрын
You can write code that happily ignores potential errors. Exceptions forces you to at least acknowledge they exist, even if you then ignore them.
@justgame5508
@justgame5508 6 сағат бұрын
@@chaos.corner Your just objectively wrong. There is literally nothing that forces you to catch exceptions or even acknowledge they exist (in most languages). Errors you have to handle them, if you chose to ignore them that’s on you, but your code has to handle them in some way. Exceptions can be fully ignored.
@yyny0
@yyny0 6 сағат бұрын
@@justgame5508 When you forget to catch an exception, your program crashes with a nice error message and stack trace. When you forget to check for an error, your program keeps running, might end up in an invalid state, and when it eventually crashes, you have no idea what causes the state to become invalid.
@chaos.corner
@chaos.corner 5 сағат бұрын
@@justgame5508 Fair enough. I was thinking of Java which requires you to either catch exceptions or declare that you throw them. I would consider not requiring that a substandard implementation of the idea. (at least for higher level languages. Low level languages are a free-for-all)
@justgame5508
@justgame5508 10 сағат бұрын
I use exceptions in exceptional circumstances, errors everywhere else
@gracicot42
@gracicot42 13 сағат бұрын
Saying that your new will not clean up and your files handle won't be closed in C++ is like saying rust is unsafe because you can use the unsafe keyword
@eldritchcookie7210
@eldritchcookie7210 13 сағат бұрын
no, on my comp 2 class the teacher commonly used new
@mariushusejacobsen3221
@mariushusejacobsen3221 8 сағат бұрын
​@@eldritchcookie7210 Your comment is the equivalent of "English is a very simple language, because all my teacher knew and could teach me is how to say 'Hello my name is ...' ". He's incompetent, and at best, he's teaching how C++ was used in the early 90s, including giving his students many bad habits. Unfortunately there's many of them.
@defeqel6537
@defeqel6537 12 сағат бұрын
Ahh... error handling, the bane of our existence.. should you return, throw, exit, pause the stack, report to the user, log the error, etc.? How much context to include? stack trace? specific variable states? And how much is all of that allowed to affect performance?
@stysner4580
@stysner4580 12 сағат бұрын
Only exit if the program depends on a certain value to be within some parameters or some operation having ran. In all other cases it depends on if a default value is acceptable. What's left then is deciding if the error needs to be logged.
@brandonpearman9218
@brandonpearman9218 12 сағат бұрын
Prime is talking in context of his experience, which is not the most common. I usually use a mix of the two because when writing a HTTP APIs. Exceptions are fine in my case because 1. the services are stateless, and 2. there are a large number of cases where I want to immediately terminate the request. Exceptions allow you to very quickly achieve the desired behavior of returning 404, 409, 400, etc. without any care for depth of call stack. Result types are nice when you want to pass the termination decision to another class. Personally I like error result at the infrastructure layer so that my application code makes that decision, it keeps everything in one place.
@stysner4580
@stysner4580 11 сағат бұрын
Ah a web dev talking about "experience". Classic.
@brandonpearman9218
@brandonpearman9218 10 сағат бұрын
@@stysner4580 I dont only do web but it is my experience in professional env with massive systems. I cant make statements in other areas such as infrastructure, tools and games because my experience there is based on small applications. I presume you dont have exp in large server side web apps, otherwise you wouldnt make that comment. Thats fine but my point is that tools dev is a small industry compared to web. So giving advice from that narrow experience band as if it applies everywhere is incorrect for most devs. Such narrow views is also bad for new devs coming into the industry (It seems that most of his viewers are students, juniors and academics).
@andreaselfving2787
@andreaselfving2787 13 сағат бұрын
errors aren't necessarily exceptional
@klasus2344
@klasus2344 13 сағат бұрын
C++ guidelines: *I.10: Use exceptions to signal a failure to perform a required task* *Reason* : It should not be possible to ignore an error because that could leave the system or a computation in an undefined (or unexpected) state. This is a major source of errors. (...) *Note* : We don’t consider “performance” a valid reason not to use exceptions. * Often, explicit error checking and handling consume as much time and space as exception handling. * Often, cleaner code yields better performance with exceptions (simplifying the tracing of paths through the program and their optimization). * A good rule for performance critical code is to move checking outside the critical part of the code. * In the longer term, more regular code gets better optimized. * Always carefully measure before making performance claims.
@EVanDoren
@EVanDoren 13 сағат бұрын
+billion
@diadetediotedio6918
@diadetediotedio6918 8 сағат бұрын
Yet, C++ is implementing errors as values. I don't think they are much convinced with that.
@davidshelton1898
@davidshelton1898 9 сағат бұрын
I'm confused if OP was talking about errors in production or dev? They are just fundamentally different and they seem to swap back and forth between those two perspectives where convenient for their argument. exceptions, asserts, try catch, etc all have their place. Seems more like architectural issues.
@yellingintothewind
@yellingintothewind 8 сағат бұрын
C++ makes exception handling quite straightforward. Do you have any state that will not be cleaned up by stack deallocation? Do you call any functions _not_ marked noexcept? Use try/catch. The reason there are so few try/catch in that database example is because the answer to the first question is almost always "no". This is because C++ heavily uses RAII, where you either get an exception while trying to allocate resources (and the resource itself will clean itself up before throwing), or you obtain the resources in a state where stack deallocation will automatically clean them up. This means exception handling mostly needs to handle rollbacks of things that need to be atomic but aren't naturally atomic.
@s3rit661
@s3rit661 11 сағат бұрын
Btw I must say this, there's a whole misconception about what errors and expcetions are, and people think they are the same thing. In oop an exception is usually related to error in business logic, not in system errors, and exception catching gives useful tools to handle them, but they can be used also for error handling A proof of that is agile that literally makes you write the exceptions on the business logic inside the documents, bc yes, you can create custom exceptions
@ammarhusain6235
@ammarhusain6235 12 сағат бұрын
"Because you would have to have the understanding of what came before it" "I think the apple's rotten right to the core From all the things passed down From all the apples coming before"
@GeorgeN-ATX
@GeorgeN-ATX 3 сағат бұрын
(In a Mario accent): "It's a me, It's a me - result_object!" -ThePrimeagen colorized 2024
@snorman1911
@snorman1911 12 сағат бұрын
Exceptions let you put one catch at the entry point of your app that says "an error occurred" if anything happens. Boom, done
@yyny0
@yyny0 5 сағат бұрын
I prefer the syntax of error values but the semantics of exceptions.
@ShootingUtah
@ShootingUtah 7 сағат бұрын
Ok so where am I wrong here? Go for example forces the checks of error values basically everywhere. How is this different than try-catching EVERYWHERE? If you write the checks for exceptions as often as Go forces you to check for error values aren't you essentially in the exact same situation? You can handle the exceptions in the exact same way?? Right? So really Prime seems to just like that languages with errors as values FORCE the check throughout the code where with exceptions you have the choice of sending them up the call stack by not checking them?? Am I lost?
@yannick5099
@yannick5099 13 сағат бұрын
Exceptions have one big advantage: automatically including the context (stacktrace). Errors as values mean you have to do it yourself, otherwise you just get something like: „file XYZ doesn’t exist“. Also just having exception handling at the top level and the rest of the code being the happy path is nice if all you can do is fail most of the time.
@hanifarroisimukhlis5989
@hanifarroisimukhlis5989 13 сағат бұрын
And that's also why i don't like exception. I want to control when and where to put stacktrace, it adds unnecessary bloat and slowdown. You know how slow (in Python) catching KeyError is instead of testing if key is in dictionary?
@youtubeenjoyer1743
@youtubeenjoyer1743 13 сағат бұрын
@@hanifarroisimukhlis5989 don’t bring python as an argument. It’s a terrible language with a terrible interpreter.
@yannick5099
@yannick5099 12 сағат бұрын
Sure, errors give more control, but also requires active work to get the same infos and discipline to get the same behavior everywhere. The Python thing is another point, the language itself is slow and this case could be solved with a better API. If there are cases that are highly likely to fail errors or in this case a bool if it exists (multiple return values) are preferable. If my web app can’t connect to the database or the result set doesn’t contain a required column then the request fails. No need to repeat error checking for every tiny operation that fails. Nothing the code can recover from. The price you pay is performance in the case of an exception. Totally fine it that is actually the exception and not the norm.
@theevilcottonball
@theevilcottonball 12 сағат бұрын
Well one can think of a ? operator that attaches context automatically.
@jonnyso1
@jonnyso1 11 сағат бұрын
That assumes you keep bubling the error up instead of handling right then. If you're calling the function, you know the context, then depending on the situation you decide wether you handle the error right then and there or if you need the caller to decide, if its the latter, usually you'd repackage the error into something that makes sense to the caller of your function. In rust that measn I just do a bunch of map_err(...)?, its pretty straight forward and quick , doesn't bloat or obscure anything, it just flows from one result to another smoothly.
@youtubeenjoyer1743
@youtubeenjoyer1743 14 сағат бұрын
The biggest problem with exceptions is runtime polymorphism. Ironically, golang has the exact same problem with its Error interface, but without any of the benefits.
@s3rit661
@s3rit661 13 сағат бұрын
Java has reflection to check the type of the expression and Wildcard to restrict the type, that's not a good point in my opinion
13 сағат бұрын
nice pfp
@yyny0
@yyny0 7 сағат бұрын
HARD disagree. There is a reason why Rust `Error`s have a downcasting API.
@chauchau0825
@chauchau0825 4 сағат бұрын
Need to point out an obvious fact: There is no silver bullet. Sometimes, using exception is better. Sometimes, using result is better. Both have it's own pros and cons. Choosing which one to use is a matter of taste or design decision based on whatever constraints you prioritize more. Everything is a Tradeoff and that's ok.
@Ulvhamne
@Ulvhamne 12 сағат бұрын
My personal experience with working with servers is that the code is stateless and operates on data that passes through the functions. If things break, winding up the stack doesn't really break things more than error returns. You will back out to the place where you can actually handle the error if you are not doing things wrong. I don't care much about which type of errors I deal with, but I want the errors that I can get when calling a function to be defined so things just don't go pear shaped in ways I didn't even know they could. But for most of the web crap, doing anything with errors seem a bridge to far and just return a 500 or 400 error, at best.
@johansmith2840
@johansmith2840 10 сағат бұрын
IMPORTANCE OF ERROR HANDELING === your software is going into a plane and a medical machine that your your friend has to fly in to get to the hospital to use the machine or " HE WILL DIE " returns " I'M GLAD THOSE ERRORS WERE FOUND OTHERWISE THE PLANE WOULD HAVE CRASHED AND THE MACHINE WOULD HAVE MALFUNCTIONED "
@retropaganda8442
@retropaganda8442 12 сағат бұрын
I prefers errors as values that are exceptions, like in C++ 😂 The automatic cleanup during unwinding thanks to value semantic is totally ok. I don't understand the debate
@yyny0
@yyny0 7 сағат бұрын
The blog post should just have been this. "Exceptions" _should_ be errors that syntactically look like normal return values but use a more efficient calling convention optimized for error propagation (i.e. `try` in Zig/`?` in Rust). Most of the arguments in favor or against exceptions do not address this.
@Diamonddrake
@Diamonddrake 11 сағат бұрын
Exceptions are just part of the throw/catch paradigm which solves the need in the most used case of goto, breaking out of something from many places and handling it in one. It can solve the issue of having to do the same thing In 5 places. Id prefer a mix of both when appropriate. I want throws (not just exceptions) and errors as values, maybe an even catch-into operator.
@hackmedia7755
@hackmedia7755 5 сағат бұрын
I have a proposal. 1/0 = 1 If you took a pie and attempted to not cut it. It will leave you with 1 pie. 1 pie, cut 0 times. equals 1 pie.
@georgeindestructible
@georgeindestructible 12 сағат бұрын
The kernel doesn't really let the system go oom, the app may freeze for a while if not written to take account for the fact that the kernel has reserved memories to avoid going oom and completely locking up.
@stysner4580
@stysner4580 11 сағат бұрын
...Yes but it will still mean you can't allocate any more memory. From the scope of the app the result is the exact same.
@georgeindestructible
@georgeindestructible Сағат бұрын
@@stysner4580 True. This is why i said thatthe program may freeze after that, it's usually how the OS handles that part.
@TimothyWhiteheadzm
@TimothyWhiteheadzm 13 сағат бұрын
In my rust programs, I would use expect for necessary startup variables and ban the use of unwrap. Generally I prefer passing up error values and handling them sensibly as far as possible. But my rust experience is limited to particular types of programs and my usage might change if I get into a different area.
@TOAOGG
@TOAOGG 11 сағат бұрын
I'm fully on the side of errors as values, but with the exception for "unmanaged" code - e.g. 3rdparty code that I cannot look into and where I have to expect the worst. E.g. if I'd provide a plugin option to my application, I'd probably wrap the calls in try{} catches as a safety barrier.
@olafbaeyens8955
@olafbaeyens8955 10 сағат бұрын
Handling OutOfMemory can be resolved by allocating some memory block during the startup of your application and use this memory to do some error recovery/reporting. E.g. you could free that allocated memory and then your code can recover.
@alexsmart2612
@alexsmart2612 7 сағат бұрын
I don't quite understand the debate about exceptions vs result types. Aren't they literally isomorphic? How are "Result method()" and "T method() throws E" anything but exactly equivalent?
@gtdcoder
@gtdcoder 7 сағат бұрын
Precisely. The only difference is that the function has to return an error instead of throwing it.
@yyny0
@yyny0 6 сағат бұрын
Exceptions are more efficient because they don't have to be checked at the call site. In your first example, the compiler HAS to insert machine code to check if the Result was an error. In the second, the compiler could perform all sorts of optimizations. My perfect programming languages would have CHECKED exceptions, where you are required to specify exceptions in function types, and required to specify if an exception will be handled (`orelse`/`match`) or propagated (`try`/`?`), but without any checks in the happy path.
@alexsmart2612
@alexsmart2612 6 сағат бұрын
@@yyny0 I was talking from the developer experience/usability perspective, since that is what this debate seems to be primarily about.
@Peregringlk
@Peregringlk 6 сағат бұрын
Exceptions are easier to work with if you have not too many points of failure, or you can handle different exceptions in a single place with some generic error handling mechanism, like printing and that's it. Error codes are easier to work with if you need a very specific behaviour for each different point of failure.
@minastaros
@minastaros 5 сағат бұрын
@alexsmart2612 Result needs to be handled by the caller, while exceptions can propagate several levels up until their type is caught (or better: catch-ed). So you can build deep nested function call hierarchies, and handle a special type of error up in a central place, relieving all functions down from that burden - _if that is useful to do so_ .
@TurtleKwitty
@TurtleKwitty 11 сағат бұрын
The trick to handle OOMs for program that might run into it: Pre alocate a chunk of memory, when you get an OOM you deallocate it and gracefullyshutdown doing what you need to with that newly accessible memory
@lucaszapico926
@lucaszapico926 9 сағат бұрын
Hey Prime, I really appreciated this conversation around error handling. Thank you!
@oggatog3698
@oggatog3698 2 сағат бұрын
It sounded like at one point you said something to the effect of "well, of course you'll still want a try...catch at the top level to make sure you can catch errors that you weren't expecting", but this means you're using two error handling paradigms instead of one. The boilerplate, while you said it doesn't matter, is still logic that your brain has to parse and occupies screen real estate that other code could occupy instead. If you're blending monads and simple bool returns that's an additional complication to track. Using try...catch also means you're able to avoid checking the boilerplate from return values and only handling things as they become problematic -- the goal of error handling isn't to handle all errors, only errors that make a difference to outcomes.
@ReneHartmann
@ReneHartmann 7 сағат бұрын
In Ada/Spark, you can verify that your code does not throw any exception. But that's a rare approach.
@yellingintothewind
@yellingintothewind 7 сағат бұрын
Recursive fibonacci is probably the first function most people use when learning how to write tail-call-optimized code. It doesn't even require full TCO support in the language, as it only needs tail-recursion-optimization, which is much easier for a compiler to implement. His version doesn't use TRO, as the last thing before the return is an add. The TRO version uses an accumulator value passed as the last parameter to the function. The recursive function call is fib(n-1, acc+n), which is fully TRO (and by extension TCO) friendly.
@MatiasKiviniemi
@MatiasKiviniemi 8 сағат бұрын
The problem that exceptions solve relatively well is that it varies a lot what level is the correct one to resolve the error. You just let it throw until it reaches the level that knows WHY the operation was done (and usually knows the context and what error to display and how). Errors-as-values can lead to each level just adding a message resulting in a stack of "Error: method FOO returned error: Error BAR returned error...". Also depends on running envinronment, e.g. OOP, React, Node what makes most sense. PS. Also I like "crash early, crash hard"-style of initially handling those errors you know for certain how to resolve and let the rest to blow up (assuming you can do this without pissing off customers). Then you look at the logs and architect the error handling, which ones you understand, how do you handle them as a whole.
@jamesking2439
@jamesking2439 8 сағат бұрын
Doc threw an exception and sent me home with my chest still open.
@robchr
@robchr 13 сағат бұрын
What about Erlang's let it fail model? Code that fails should just be restarted and usually recovers.
@stysner4580
@stysner4580 12 сағат бұрын
...Should just be restarted?! And potentially lose all state?
@defeqel6537
@defeqel6537 12 сағат бұрын
@@stysner4580 it's functional for a reason
@qizott6442
@qizott6442 11 сағат бұрын
That is only when something unexpected happens. If you are going to crash each time doing the same thing then you are doing it wrong and you should handle the error. It is not crash and forget.
@diadetediotedio6918
@diadetediotedio6918 11 сағат бұрын
@@stysner4580 Code should be reentrant when you use these languages.
@thewhitefalcon8539
@thewhitefalcon8539 7 сағат бұрын
Sounds like exceptions
@ErazerPT
@ErazerPT 5 минут бұрын
Oh dear, my favorite pet peeve. The cheapening of "exceptional" by way of using exceptions for trivial circumstances, all because you cba to check for errors... One of our API's was doing just "happy path or 500's". Guess what, then it met the real world, where weird and funky s**t happens all the time, and you have to handle it at the point where it happens because a) many are recoverable and b) the stack trace will be missing vital info it won't capture (for debug purposes). Now the API still has a try/catch at every endpoint, but the catch has a standard form of "either this WAS a catastrophic failure of the system or YOU f'ed'up your checks, pray the error log doesn't show the later".
@lennarthammarstrom1321
@lennarthammarstrom1321 7 сағат бұрын
I don't like exceptions but when you have handlers calling handlers unpacking those errors and returning them all the way down is a pain
@Tuniwutzi
@Tuniwutzi 4 сағат бұрын
I'm 10 minutes in and already experiencing the pain I was expecting. I know exceptions are contentious and I understand why many people prefer errors as values. But Primes critique of exceptions, namely "if you want good error handling (with exceptions), you'll litter your code with try-catch", is just a skill issue. If you find your code littered with try-catch, you're doing exceptions wrong. The webserver example given by the author is perfect for this. There will be a function that handles one single request from start to end. That function will have a try-catch wrapping receive->process->respond. If an exception occurs, we can log the reason and try to send an error response. This covers 99% of your error cases perfectly. You might find some specific error cases that should be handled in particular ways, but those are the vast minority.
@ivanmaglica264
@ivanmaglica264 8 сағат бұрын
AFAIK, exceptions turn into a "goto" statement, basically. If you are doing million operations and check an error status after each one, that is a huge CPU overhead. With exceptions, you don't need to check explicitly after each operation, because it jumps to an error handling routine after an error. This is why i much prefer the try catch style of error handling.
@diadetediotedio6918
@diadetediotedio6918 8 сағат бұрын
> AFAIK, exceptions turn into a "goto" statement, basically. If you are doing million operations and check an error status after each one, that is a huge CPU overhead. Except you don't need to do it on "million of operations", you just need to architect you code in a non obviously terrible way and make the hot paths not checking it. + you are still doing a million of error check operations with exceptions, the difference is that the ones throwing are doing it and not the ones catching, and that you are paying a million times of extra cost when an error actually happens.
@yyny0
@yyny0 6 сағат бұрын
@@diadetediotedio6918 You do when you propagate the error, which is what most code with error return values end up doing most of the time. And the cost of an error is irrelevant, it should never happen and even if it does, it will grind your entire program to a halt anyway.
@diadetediotedio6918
@diadetediotedio6918 6 сағат бұрын
@@yyny0 "the cost of an error is irrelevant, it should never happen and (...)" > this is something I will need to disagree very hardly. Do you have any statistics to back up your claim that it is irrelevant? + Why exactly an error, a perfectly valid representation of a specific part of a system in a given time, never happen and what is the factr that makes it "gind my entire program to a halt anyway"?
@yyny0
@yyny0 4 сағат бұрын
@@diadetediotedio6918 An error is incorrect by definition, it is NOT a valid representation of your system, in fact, the whole point of returning an error value is to describe invalid program state. As for statistics: our production product has ~20k `throw`s across all our (vendored) dependencies (of which ~2k in our own code), and only 130 places where we catch them. Most of those places also immediately retry or restart the entire task. I would consider that "grinding to a halt". Additionally, 99.9998% of those tasks in the last hour did not return an error, so even if the cost of throwing a single error was 1000x the cost of running an entire task (which it is not), it would still be irrelevant.
@diadetediotedio6918
@diadetediotedio6918 4 сағат бұрын
​@@yyny0 > [An error is incorrect by definition, it is NOT a valid representation of your system, in fact, the whole point of returning an error value is to describe invalid program state.] What I said: ["a perfectly valid representation of a specific part of a system in a given time"], an error IS, in fact, a perfectly (in a formal sense, and sometimes in business rules) of a of a system in a given time. When you do a login and you type the password wrong, the InvalidPassword , in fact, a perfectly reasonable and valid of state in your system (as it should be, it should not panic your software, just exhibit a simple message to the user so he can type the correct password). When you call a method to write to a file, and the file do not exist, receiving a monad where the error is represented as a possibility , indeed, a perfectly valid way of representing your program state. I just don't know why are you saying this. An error could be defined as a "undesired state given a specific goal intended in a closed system", but not necessarily as an "invalid state" if it is conceived as to be a part of the representation of your possible states. dot. Proceeding. > [As for statistics: our production product has ~20k `throw`s across all our (vendored) dependencies (of which ~2k in our own code), and only 130 places where we catch them. Most of those places also immediately retry or restart the entire task.(...)Additionally, 99.9998% of those tasks in the last hour did not return an error, so even if the cost of throwing a single error was 1000x the cost of running an entire task (which it is not), it would still be irrelevant.] You said "it should never happen", and then you showed me your personal data and said it happens 20k times, I would say this is a damn high number to say it "should never happen". Also, you did not give me any timeframe, not a comparison between different services, giving me one personal example is just anedoctal example. How exactly I'm supposed to work with this and say there are not very statistically representative systems that have an impact with throwing exceptions for everything? This is a pretty specific response. > [I would consider that "grinding to a halt".] You consider restarting a program immediately after an error the same as "grinding to a halt"?
@grzesiekaz
@grzesiekaz 3 сағат бұрын
The argument about Java's OutOfMemoryError was completely wrong because Errors in Java are not even meant to be caught at all (google the difference between Errors and Exceptions in Java).
@freman
@freman 8 сағат бұрын
*blink* Exceptions - error handling is optional, you can forget about it for however many layers Errors - You're forced to acknowledge the existance of the error at every step of the way and it sits there shaming you if you just dumbly return or panic Even go has an oh shit mode, { defer recover(); panic() }
@thewhitefalcon8539
@thewhitefalcon8539 7 сағат бұрын
So exceptions are good if you want to have a top level error handler that logs and returns a 500 page
@pikzel
@pikzel 7 сағат бұрын
err := foo() err = bar() if err != nil … This is valid Go. Not even a warning that err from foo() was never read.
@Kondzitsu
@Kondzitsu 6 сағат бұрын
I mean, that's just not true. In Java you won't even run/compile a program without handling all the exceptions.
@markbertenshaw3977
@markbertenshaw3977 8 сағат бұрын
Do COM component programming, and get both paradigms in the same codebase. Intra-component C÷÷ exceptions leading to returning HRESULTS at the interfaces between components.
@blenderpanzi
@blenderpanzi 11 сағат бұрын
About C++ exception don't close file/network handles: I don't think anyone opens a file/network handle using new, even in pre-unique_ptr code. The handle is stack allocated and thus is closed in the exception cleanup. Also if the new-ed value is deleted in a destructor and the constructor of the object ran through[1], then that is also handled in the exception cleanup. [1]: Exceptions in the middle of the initialization of member variable is a problem in C++. When that happens the destructor isn't called and you can't find out which members are initialized and you leak memory/handles. I think that's a bit of a design mistake of C++. People handle that by using a member initializer list that itself only calls trivial constructors of the member variables that never throw (I guess except if OOM).
@yapet
@yapet 10 сағат бұрын
^^ this The prime’s misconception got me heated :|
@blenderpanzi
@blenderpanzi 10 сағат бұрын
@@yapet I imagine the thing with "no overhead in happy path" can also something really nice. But you need to be sure that you only throw exceptions if it's really an exceptional case. Also about the Fibonacci example: He was all about how you don't write it like that etc. That was just code simulating a deep call stack! Deep call stacks do exist. This micro benchmark shows the difference in that particular case. That's valid, IMO. Having said all that, I do like Rust more than C++. I think it's just important to correctly understand your tools.
@mariushusejacobsen3221
@mariushusejacobsen3221 9 сағат бұрын
[1] If the constructor of a member completed, and then the construction as a whole fails, each such member shall be destructed in reverse order. So how does that leak exactly? The use of raw/dumb handle types? There's a different case X::X() : m1(new Y), m2(new Y) {} which can leak because it may do both new calls before calling the first constructor. Though you would usually let the object do its own new to begin with, so that's a non-issue.
@yapet
@yapet 9 сағат бұрын
@@blenderpanzi bro, you are reading my mind. Just as I was listening to this section, and debating with myself. I am honestly too tired to start any kind of elaborate debate, but thanks for pointing this out! ❤️
@blenderpanzi
@blenderpanzi 9 сағат бұрын
@@mariushusejacobsen3221 Yeah, I might not be remembering it right. It's a long time ago where I learned about that.
@emilemil1
@emilemil1 9 сағат бұрын
At its core there are very few differences between errors as values and exceptions, it's really just a slight syntax difference, and most of the problems people have with one or the other comes more down to implementation details of the language. For example I could write a wrapper function in most exception-based languages that runs another function and returns a tuple containing either the return value or the thrown exception. I could do a similar thing in for example Go to turn any returned error into a panic. The only real difference is whether you like your default error handling to be with a try/catch or with en error check. Personally I'd like a language to support both. I really like the try/catch pattern for aggregating error handling across multiple lines, but it's absolutely awful when it's just wrapping a single line of code. I like the ability to return a value AND an error like in Go, and I like the ability to ignore that error if I'm confident that my input can't cause an error, and I also like that there is a distinction between expected and unexpected errors. What I don't like in Go is how function authors can't mark errors as "probably a bad idea to ignore" because they can be caused by side effects even with proper input. I also prefer the clarity of a typed throws declaration which tells you at a glance if and how a function may error.
@EhdrianEh
@EhdrianEh 14 минут бұрын
At 16min and can't continue. The whole article is based on a false premise that rust will crash the program. He thinks unwrap is error handling and it's not. It's literally the opposite definition of error handling in rust. So, therefore, rust programs crashing on error means that you did not handle the error and can't be compared to try/catch. We are at the strange stage in the life of a technology when people try something but don't actually learn how to use it in production - then write an article about how they don't like to use it in production.
@styleisaweapon
@styleisaweapon 9 сағат бұрын
Dont be alarmed when you hear this: exception systems are a downgrade of BASIC's "ON ERROR" exception system. BASIC's version is MORE POWERFUL for three reasons. First is "RESUME" and "RESUME NEXT" an ability that modern exception systems do not have. Second is that "exception handlers" arent guarded code paths, they are regular code that can be entered both because of the exception as well as because the programmer intended regular execution to enter it also. Third is the "ERR" object which records if there were an exception (which may have been RESUMEd) and if so what it was. This allows ALL error handling strategies simultaneously. It doesnt have to be a choice that both caller and callee agree on.
@LuizMoraes-xb7qj
@LuizMoraes-xb7qj 8 сағат бұрын
You know it is a good article when the author uses fibonacci as an example
@ladnir
@ladnir 4 сағат бұрын
When you get an oom, you free stuff in the unwind. Then proceed.
@igorordecha
@igorordecha 3 сағат бұрын
What I get from it is that Prime likes errors as values only because you have to encode them in the type system (like as a union type or something) whereas exceptions are essentially untyped. Even though im on team exceptions I kinda agree. Remember this "colored functions" article a while ago? It was about async functions and how if you want to use a single async function the entire stack has to be async. We should do the same thing for exceptions in typescript. If you import a function that didn't type what it can throw then you have to catch all exceptions. It would be like a function returning any type from a vanilla JS function. However if you import a typed function (in this new system, with typed exceptions) you only have to catch those specific errors. And if you don't catch them all, then your function automatically get typed as throwing all the remaining errors.
@RuddODragonFear
@RuddODragonFear 9 сағат бұрын
Arguing against errors as values and bringing up null+ERRNO as argument is an example of a motte and bailey doctrine. Errors-as-values can be as rich as one wants. They can even include the entire stack unwind if the programmer so chooses. In Rust, as in Go, almost all error handling can even query for a chain of *causes* with very rich information as to what actually caused the error (though arguably it's not wise to handle an error so up the stack that calling .cause() becomes necessary). The article writer is fractally wrong.
@RuddODragonFear
@RuddODragonFear 9 сағат бұрын
Valid criticism he raised: userspace Rust programming needs to handle the low level details of recovery of out-of-memory conditions at least as well as C. It's not cool for systems software to expect Box::new() to always succeed and panic otherwise. But in all other respects, Rust is miles ahead of the competition. Handling an OOM is not possible under default Linux settings, because the OOM killer just kills your process, no chance for recovery... but if a process is given an ulimit of memory equal or lower to the OOM limit set on the cgroup, then the process actually will get an error when attempting to malloc(), and then the process can gracefully recover (how tricky that might be ranges from very trivially unwinding the stack in Rust to impossible in a GC language). But without the ulimit set on the process, no chance. OOM just kills your process.
@pizza-cat1337
@pizza-cat1337 2 сағат бұрын
I kind of think returned errors make more sense in low level stuffs. If you work on a business type of application, where features, use cases are what matters, you don't want to pollute the happy path/expected scenarios with "if err != nil" all over the place, you want the code to look like the actual use cases.
@fusedqyou
@fusedqyou 14 сағат бұрын
The author of this article clearly never got past making a todo app.
@kieranmckenzie2995
@kieranmckenzie2995 4 сағат бұрын
The discussion is already over at unchecked exceptions and having ever used JS ever. Yay any random function can die any time and I don't know. Hey Java fixes this, oh wait its just "coloured" again. If your language has functions that make me handle the error then its a win, if it doesn't its a fail. Lets clear that bar first yeah? We all lived in this guys world at some point whenever we used JS we already know what its like. Get there however you want just tell me what the hell can throw/error.
@worldwarwitt2760
@worldwarwitt2760 54 минут бұрын
Stack unwinding is heavy. However, the stack unwind, providing the higher level code is written correctly, will call the __finally block, or catch, and clean up. However, in languages like C# , "using", guarantees cleanup on exceptions as it is in an implicit try, finally, without a catch.
@awesome.rats159
@awesome.rats159 11 сағат бұрын
For the automatically cleaning up when an exception throws, i assume the author is referring to java or python (and soon to be js with `using`), where you can specify resources that will automatically be closed when the block ends. Also, I think prime might be sullied by the sorry state of JS error handling. In a language like Java, you can chain multiple catch blocks for different types of exception, so each specific exception is acknowledged and you know when a method you call may have an exception. Now, the main real issues with exceptions to me is how much they rely on the programmer to be smart. The idea behind checked/unchecked exceptions make sense if you assume the programmer always uses unchecked exceptions when they want to hard crash, and checked exceptions when they don't. But the fact that you can make your exception unchecked without thinking about if it should be or not, and you can also do stupid stuff like catching an `ArrayIndexOutOfBoundsException` or an `OutOfMemoryException`
@yyny0
@yyny0 5 сағат бұрын
Developers do stupid stuff with error values just the same, like rethrowing errors without their context. Also much more common from my experience in Rust.
@xxapoloxx
@xxapoloxx 8 сағат бұрын
Actually in Java, you can force the developer to handle the potential exception.
@SG_01
@SG_01 12 сағат бұрын
Ugh, c++ exceptions aren't even guaranteed to be of a specific type. And they require rtti and all the overhead that comes with that.
@s3rit661
@s3rit661 10 сағат бұрын
Java ones are
@diadetediotedio6918
@diadetediotedio6918 8 сағат бұрын
@@s3rit661 Partially only.
@samhughes1747
@samhughes1747 11 сағат бұрын
This whole thing sounds like one of those “the only good aesthetic standard is brevity” guys. He’s making up reasons for what he wants to be true.
@KevinFromWestBrazil
@KevinFromWestBrazil 13 сағат бұрын
I thought checked exceptions were bad, but errors as values is functionally the same thing
@orterves
@orterves 13 сағат бұрын
Checked exceptions are good, actually - when developers take the time to map exceptions and handle appropriately. Languages that don't check exceptions have the same problems as languages that do, it's just that they sweep the problems under the rug and make them someone else's problem
@adambickford8720
@adambickford8720 13 сағат бұрын
No, because checked exceptions still send the control flow to outer space. Errors as values come back to the caller.
@youtubeenjoyer1743
@youtubeenjoyer1743 13 сағат бұрын
@@adambickford8720 but when you have a complex system, every high level function call can fail. At that point you either use a macro, or if-err-return-err like a drooling gotard.
@slr150
@slr150 13 сағат бұрын
Checked exceptions are better than error values because they preserve failure stack traces when they are passed on.
@krux02
@krux02 13 сағат бұрын
@@adambickford8720 that is exactly the reason to use exceptions. If you don't want that, return errors as values.
@phillipsusi1791
@phillipsusi1791 5 сағат бұрын
"This is a good argument"? Is it? Assuming you actually configure your kernel to not overcommit memory so that malloc() actually fails, and that gets turned into an exception, and your exception handling is trying to allocate memory to add context information to the exception at each step up the call stack, then aren't those allocations also going to fail? And what happens when you get an exception while handling another exception? Oh right, you terminate().
The Good And Bad Of C++ As A Rust Dev
35:34
ThePrimeTime
Рет қаралды 55 М.
Editor/IDE Tier List
1:40:07
ThePrimeTime
Рет қаралды 368 М.
Amazing Parenting Hacks! 👶✨ #ParentingTips #LifeHacks
00:18
Snack Chat
Рет қаралды 20 МЛН
So Cute 🥰
00:17
dednahype
Рет қаралды 59 МЛН
Zen 5 And AI Doom w/ Casey Muratori
1:49:58
ThePrimeTime
Рет қаралды 103 М.
Designing My First Game w/ Casey Muratori
1:42:46
ThePrimeTime
Рет қаралды 159 М.
Amazon Says Return To Office Or Get Fired
59:33
ThePrimeTime
Рет қаралды 156 М.
DHH Is Right About Everything
2:02:54
ThePrimeTime
Рет қаралды 190 М.
The complete and utter collapse of Ubisoft
31:41
Legendary Drops
Рет қаралды 875 М.
What GenZs Think Of Software Engineering
2:05:46
ThePrimeTime
Рет қаралды 232 М.
what is wrong with rust and linux????
57:15
ThePrimeTime
Рет қаралды 180 М.
I made JIT Compiler for Brainf*ck lol
3:07:56
Tsoding Daily
Рет қаралды 80 М.
Eric Weinstein - Are We On The Brink Of A Revolution? (4K)
3:29:15
Chris Williamson
Рет қаралды 4,3 МЛН
Plagiarism and You(Tube)
3:51:10
hbomberguy
Рет қаралды 25 МЛН