err != nil Is GOOD? (And Why)

Поділитися
Вставка
  • Опубліковано 22 гру 2023
  • Recorded live on twitch, GET IN
    / theprimeagen
    Become a backend engineer. Its my favorite site
    boot.dev?promo=PRIMEYT
    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?: / theprimeagenreact
    Kinesis Advantage 360: bit.ly/Prime-Kinesis
    Hey I am sponsored by Turso, an edge database. I think they are pretty neet. Give them a try for free and if you want you can get a decent amount off (the free tier is the best (better than planetscale or any other))
    turso.tech/deeznuts
  • Наука та технологія

КОМЕНТАРІ • 592

  • @DerekDoes...
    @DerekDoes... 5 місяців тому +71

    Love this a lot more than the typical 'reacting to videos' videos. Need like a playlist to be able to find these kinds of talks.

  • @nisancoskun
    @nisancoskun 5 місяців тому +336

    In js, most libraries(native ones as well) throws errors that you may not know about. Sometimes this errors aren't even the native Error type. In Golang you know exacly what kind of error you are expecting, I like that explicitly.

    • @retishe7660
      @retishe7660 5 місяців тому

      nice website 👌

    • @amotriuc
      @amotriuc 5 місяців тому +14

      libraries should specify what errors they should the throwing, if they don't it is a crappy library.

    • @mdbk2
      @mdbk2 5 місяців тому +25

      @@amotriuc Or the language is crappy if everyone can just ingore errors.

    • @amotriuc
      @amotriuc 5 місяців тому +7

      @@jww0007 what about them? See Java /C# Languages they do exception handling quite well. Including those ones. Bad ones are C++ JavaScript.

    • @amotriuc
      @amotriuc 5 місяців тому +3

      @@mdbk2 it is much easier to ignore a return code then an exception. If you miss an exception it will be caught on top level so you know it did happen. if you ignore result code no1 will know it did happen.

  • @JoshuaMaciel
    @JoshuaMaciel 5 місяців тому +179

    I absolutely love Go error handling. Honestly I hated error handling when I started learning programming because I started with JS. When I started Go, I hated the error handling until one day after writing a service in it and realized how nice it is, looks bad but who cares? It works well. I agree though that Rust does error handling better but Go’s is super simple along with everything else about the language

    • @NithinJune
      @NithinJune 5 місяців тому +1

      this!

    • @raenastra
      @raenastra 5 місяців тому

      agreed - in the big picture, syntax is pretty trivial; what it enables you to do matters more

    • @MomchilAtanasovHome
      @MomchilAtanasovHome 5 місяців тому +2

      Though rust error handling does a bod job about source of error unless you set the proper env variable in debug. With Go and wrapping, you get better pinpointing. From what I recall, Zig has the best error handling - the best from both worlds.

    • @Tony-dp1rl
      @Tony-dp1rl 5 місяців тому +1

      Disagree completely. Writing exception handling in Go is awful, because there is so much code that is NEVER executed, but is just there "in case" ... Just yuck.

    • @MomchilAtanasovHome
      @MomchilAtanasovHome 5 місяців тому +5

      @@Tony-dp1rl what do you mean by code that is never executed? If you know that it is never executed, just use underscore to ignore the error, otherwise it is a branch worth handling.

  • @datguy4104
    @datguy4104 5 місяців тому +93

    Then ironically when you use the return value from the JS function that could be a value or null, you'd still have to do an "if value === null" and handle that case too lol

    • @ekszentrik
      @ekszentrik 5 місяців тому +1

      What a stupid post upvoted by people who don’t use JS/TS.
      Null is never a native output in JS. It doesn’t happen on its own. Some programmer simply decided to use the “null” object.
      All empty references in JS are undefined.
      Don’t blame the language because your coworker or your own stupid face decide to return “null” objects either due to a cargo cult believe that value is useful, or because they need to signify an assigned but unused value.

    • @v0xl
      @v0xl 5 місяців тому +1

      by the way, null is the only case when == is preferable over === since it also handles undefined. (*only* undefined + null)
      aka x == null.

  • @noamprag9393
    @noamprag9393 5 місяців тому +69

    Both Go and Rust use monadic error handling: Rust uses the Result monad while Go utilizes a monad known as "Writer" (which basically means a value with some other accumulated value, which in this case is the error). Rust is better in this sense because you cannot represent invalid states. On the other hand: in Go you could return `(nil, nil)` or both an error and a value. The result of this, is that error handling in Go feels more imperative and in Rust more declarative.

    • @Daniel_Zhu_a6f
      @Daniel_Zhu_a6f 5 місяців тому +4

      writer is when you append things to a list, hashmap, etc. (err, value) is just a pair, ie an anonymous product type. writers are useful outside of FP languages when you want to accumulate multiple errors or accumulate operations and execute in bulk or mock io.

    •  4 місяці тому +1

      Agree, add to this that the Go compiler does not encourage (force) you as a programmer to handle the error case, it can be happily ignored.
      Whereas Rust compiler forces you to handle all cases making it impossible to access the return value (compile-time error) without checking for an error first. This is a huge win.
      The `Result` type and pattern matching in Rust are simply awesome.

  • @mctechcraft7
    @mctechcraft7 5 місяців тому +1

    One thing I did in my C# code was add an extension method to Task that just is a wrapper around a try catch that returns a (T, Exception?) tuple that lets me do this and wrap existing methods so that I never get exceptions

  • @IvanRandomDude
    @IvanRandomDude 5 місяців тому +84

    Exactly. It is comparing apples to oranges. Equivalent Go code to "const val = await foo()" would be "val, _ := foo()". See, it is even shorter and easier to read :)

    • @zaneearldufour
      @zaneearldufour 5 місяців тому +3

      Aren't panics way worse to deal with than JavaScript errors?

    • @ThienNguyen-bg1kx
      @ThienNguyen-bg1kx 5 місяців тому

      @@zaneearldufour not if we talking about a panic in http handler with proper recovery middlewares

    • @Gusto20000
      @Gusto20000 5 місяців тому +4

      @@zaneearldufourhow exactly you get panic from val,_ := foo()? Or you think it won’t panic if you write val, err := foo()?

    • @arturfil
      @arturfil 5 місяців тому +18

      @@zaneearldufour You don't use panics for error handling typically, you use panics for situations where you can't keep on running your program and thus you panic and see the stack trace of where it panics. I.E when you don't set the port properly for a server (use letters for example) and you have to stop the server so that you correct that before you keep on going with running the program.

    • @VivekYadav-ds8oz
      @VivekYadav-ds8oz 5 місяців тому +1

      @@Gusto20000 I have not written Go (I do write Rust btw), so I don't know what's the value of `val` when err == nil. But isn't the only logical choice being that it must be nil? In that case, using val should result in some sort of panic shouldn't it?

  • @BrianGwaltney
    @BrianGwaltney 5 місяців тому +100

    Having coded full time in js/ts for 5+ years and 3 months in go, I'm 100% convinced go's way is better. Never want to go back having build production apps in both. In go, I know every error has at least been acknowledged if not handled correctly.

    • @natescode
      @natescode 5 місяців тому +9

      Go doesn't force you to handle it. Just write a bunch of ugly IF err != Nill checks. Proper wrapped error types are cleaner and guaranteed they're handled.

    • @BrianGwaltney
      @BrianGwaltney 5 місяців тому

      That's why I said at least acknowledge it. It's up to all of us to handle errors smartly. But at least I always know if a go function can error.@@natescode

    • @ocoolwow
      @ocoolwow 5 місяців тому +3

      OP should not be anywhere near a computer

    • @OnFireByte
      @OnFireByte 5 місяців тому

      @@natescode Every languages have the way to write ugly error handling. It's just that TS never tell you if it gonna throw or not. Go at least shows explicitly that error is possible without using any LSP, and if someone write ugly check it's just gonna be very obvious.

    • @aazzrwadrf
      @aazzrwadrf 5 місяців тому +6

      @@OnFireByte It's not explicit when the majority of nontrivial functions in your program are propagating an unknown union of errors up the stack. The issue with error handling in Go is you don't know what errors are being returned. It's nothing like Result in Rust.

  • @einargs
    @einargs 5 місяців тому +7

    I like errors being part of the return type. My problem is that instead of using typed unions / associated value enums / algebraic data types, which is the correct, type safe way to deal with this, where accessing the value requires you to branch on having an error or not... they made it a tuple of nullable values.
    I guess it's good for a simple language that doesn't want extremely basic and important features for modeling data like being able to represent mutually exclusive states. (I'm salty.)
    That said, e.g. LLVM does errors like Go (with a custom wrapper return type) and it's really fun.
    On the other hand, I also really like Elixir style error handling. Elixir is built to do incredibly fault tolerant distributed systems where lots of things can error. So the idea is don't handle individual errors; any time there's an error, just recover from a known good state.

  • @Grouloo
    @Grouloo 5 місяців тому +10

    Although returning a tuple is wayyyy better than throwing and catching, I still find it really awkward to handle. I think Rust and functional programming languages got it right by using a Result wrapper type / monad. That way you are sure that is one or the other and it cannot be the error and the value at the same time, moreover, you get methods and functions to deal with error handling in a really logical and secure way.

  • @vladislavkovechenkov8473
    @vladislavkovechenkov8473 5 місяців тому +1

    Thanks a lot for the vid. I'm only starting with Go and I was quite confused that error handling in Go was criticized this much, as to me it looked fine after years with JS. Yes, annoying, but as someone said on the internet "at least it makes you pretend you thought about how to handle potential errors", which sounds like a win for me. Maybe I haven't yet written enough Go and I'll have a better idea of where this pattern falls short as I run into more cases of handling errors, but for now I'll move forward with confidence writing Go and not stress about that error handling is "ugly". Also, thanks to everyone who added perspective in the comments sections, I learned a lot while reading about nuances of this pattern!

  • @themichaelw
    @themichaelw 5 місяців тому +56

    If you've ever worked professionally in Go _and_ in another language without error-by-value, I strongly think that most people would prefer EBV. It significantly reduces the cognitive overhead when you need to jump into an unfamiliar service/repo. No surprises on where errors might pop up from.

    • @reddragonflyxx657
      @reddragonflyxx657 5 місяців тому +12

      I like Rust's Option and Result much more than Go's err. Usually (in code I write) if I'm handling an error I'm dealing with data from the code that may have encountered an error. A Result makes me decide how I want to deal with errors before I can get at the data (I can return the error with "?", panic on errors with ".unwrap()", convert to an Option with ".ok()", map Ok()/Err() values, do clever iterator stuff with arrays containing error values, and otherwise gain a lot of flexibility by having the error and data bundled together).

    • @tsanov86
      @tsanov86 5 місяців тому +1

      Before Go I did a bit of Delphi and C. I hated try-catch in Delphi, I didn't know where to put, I didn't know where exactly it came from and if you did not set it right initially it was even worse to find what is happening in production. C was such a breath of fresh in regards of errors - very simple and understandable, but really annoying to deal all the time with memory allocations and leaks so when I found Go and had the chance I switched all the codebase to Go in the next few years. This was 5-6 years ago, I still think it was the right call. I have colleagues that when from Delphi to C# and to this day we have this debate where they consider try/catch superior

    • @orterves
      @orterves 5 місяців тому +3

      I am primarily a C# developer. Exception < (value, Err) < Result, simple as that. The problem with exceptions is as soon as they are possible in a language, they can happen anywhere and everywhere, for any reason, without warning. They are awful for having certainty about how the program will behave.
      Results are superior especially when the language has the ? or equivalent operator to pass the handling up the chain and otherwise enforces the handling of both cases, but I frequently try catch Exceptions in C# as early as possible to convert into a Result type just to get a little control back

  • @mmmk189
    @mmmk189 5 місяців тому +9

    If you want to handle errors by value in javascript you can adopt that convention and return a tuple like in go. Then the code will look very similar to go. The code in this video is complex because you are translating error via exceptions into error via return value. If you use exceptions all the way through then in javascript you will just do .catch on your calls to foo and bar and do your handling in there which is imo a lot cleaner than go

  • @eNtrozx
    @eNtrozx 5 місяців тому +15

    Yeah but the annoying things is that if you want the error to bubble up, you still have to handle it

    • @ficolas2
      @ficolas2 5 місяців тому +2

      Which is one of the reasons why rust does a much better job, you can just use the question mark

    • @SandraWantsCoke
      @SandraWantsCoke 5 місяців тому

      yes, but you simply return the error

    • @eNtrozx
      @eNtrozx 5 місяців тому +2

      @@SandraWantsCoke That's handling, you write actual code that does that

    • @PamellaCardoso-pp5tr
      @PamellaCardoso-pp5tr 4 місяці тому

      At this point people just need to start implementing their own Maybe/Either/Result Monad, its not that hard at all, its actually insanely simple, Just a couple 5-6 functions and you're good to go.
      Monads can be implemented in any language and they're insanely usefull (Just look at rust)

  • @amirhosseinsoleimani5410
    @amirhosseinsoleimani5410 5 місяців тому +38

    Absolutely right! The messed up part is, lot of Devs prefer smart looking or cleaner code over secure or more stable code! Btw I love go's error handling or generally, "Errors as value" over old school "Exceptions"

    • @dealloc
      @dealloc 5 місяців тому +5

      Old school would be to pass the error through arguments, or return an int with negative values, and is arguably worse; If you need the error message you then have to call `strerror(errno)`. Couldn't be anymore magic in my eyes.
      The problem is not exceptions but the language's ability to provide proper ways to deal with them and convey information about them. A language should provide ways to mark functions and things like pattern matching, operators, guards, etc. to handle them. Not just try/catch, which is arguably the worst way to deal with exceptions.

    • @LusidDreaming
      @LusidDreaming 5 місяців тому +6

      @@dealloc try catch is what makes exceptions what they are. Maybe there is a different syntactic way to express this, but exceptions (in the context of things you "throw") will always have the undesirable behavior of being inherently different than other normal values. Its the ability to throw something up the callstack implicitly that (in my opinion) causes issues, not just the scoping issues with try catch blocks. Being able to throw an exception is essentially a goto statement without an explicit label. They can make the control flow of an application hard to understand and they inherently break locality of behavior. They are also harder for static analysis tools to reason about.
      I will say that Java forcing explicit definitions of what exceptions may be thrown from a method at least gives more explicit information and helps with static analysis. But it still suffers from the non-locality of behavior issue. Having errors as values means you don't need separate constructs to handle errors. They can be handled like any other value in your system, which leaves a lot more flexibility in how you handle your errors. Even when exceptions are values (like being objects in Java), you still have to use special syntax to extract them and pass them around, and I've rarely seen that ever done. You essentially create two separate control flows for your system, one of which is implicit.

  • @MatthewAhrensTech
    @MatthewAhrensTech 5 місяців тому +1

    Either your effects are in your types (tuples, monads, a failure ADT) or they are in their own effect system (exceptions, signals)
    I'm glad you mentioned a monadic approach. It comes down to the same tradeoff:
    I want control -> handle it all in line
    I want ease of expression -> abstract error handling from the "happy path" computation
    Different tools for different jobs

    • @Luxalpa
      @Luxalpa 5 місяців тому

      Or use Rust and just get both?

  • @Im_Ninooo
    @Im_Ninooo 5 місяців тому +4

    I agree, but sometimes it's nice to just not care about handling specific errors and just put a try-catch block in the main function, specially during prototyping.

  • @PerfectMakanju
    @PerfectMakanju 5 місяців тому +1

    This days I tend to use a library like purify-ts to wrap my Promise codes in Javascript. I find it insane not to be able to reliably know what errors a function throws or even be forced to handle it. This however means that I'll have to be using creating more functions to handle errors (like .then promises handles)

  • @theowenmccarthy
    @theowenmccarthy 5 місяців тому +51

    To be fair, as someone who regularly grumbles about Go's error handling I'm not advocating for it to be more like JavaScript's with Try catches, I just want like a '?' operator that would be the equivalent of "if err != nil {panic(err)}" and another like "?{return err, ...}" that would be the equivalent of "if err != nil {return err}" or "if err != nil{return err, nil, etc.}". When I need to do some custom error handling, Go is the best, but when I'm just testing something out or I don't expect any errors that shouldn't be returned I'd rather not have to constantly copy and paste the same codeblock.

    • @DemonButt3r
      @DemonButt3r 5 місяців тому +1

      This is all I want as well. I dream about it

    • @nskeip
      @nskeip 5 місяців тому +8

      Not sure if '?' operator should panic

    • @Aedaeum
      @Aedaeum 5 місяців тому +8

      If you're just testing stuff out, why not use the placeholder character "_" ? Then you don't have to handle the err case.

    • @MomchilAtanasovHome
      @MomchilAtanasovHome 5 місяців тому +3

      ? should not panic, it should propagate.
      The problem with pure propagation is that you have leaky abstraction. If you treat each package as standalone OS library, it should propagate typed errors from its package, hence just using ? is not applicable in that package's implementation. Rust fails at that as well.
      I would like tike have `.ErrOnFail(err, "%w: error creating database user: %w", ErrInvalidState, err)` which would propagate a wrapped error.

    • @Yoshidzo
      @Yoshidzo 5 місяців тому +1

      @@Aedaeum exactly

  • @jasonscherer2631
    @jasonscherer2631 5 місяців тому

    I learned how much this makes sense after now doing a golang code base, I love it so much more.

  • @patrickaikonia853
    @patrickaikonia853 5 місяців тому

    I love the take and I also raw dog my JSON.parse every single time unless I have more than on potential error source

  • @ashishalex10
    @ashishalex10 5 місяців тому

    love these kind of videos

  • @CodyDBentley
    @CodyDBentley 5 місяців тому

    Something we do at my job is if/else chaining related blocks, and I haven't really seen it done in other places. All the values are scoped to the entire if/else chain, and the only downside is sometimes function signatures can break the blocks if it has no error branch, but most things we write or interact with have error returns. Example (assuming foo and bar are int values or something):
    ```
    if fooVal, err := foo(); err != nil {
    return 0, fmt.Errorf("foo failed: %w", err)
    } else if barVal, err := bar(); err != nil {
    return 0, fmt.Errorf("bar failed: %w", err)
    } else {
    return fooVal + barVal, nil
    }
    ```

  • @hakuna_matata_hakuna
    @hakuna_matata_hakuna 5 місяців тому

    this is the kind of content i loved this channel for

  • @Fernando-du5uj
    @Fernando-du5uj 5 місяців тому +1

    Thank God, I was waiting for that last "...agen". I thought you will not do it.

  • @beofonemind
    @beofonemind 5 місяців тому

    thanks for hammering this home. I've been telling myself not to cut corners with error handling. I will reap the benefits of it later.

  • @LinxOnlineGames
    @LinxOnlineGames 5 місяців тому +2

    I think this moves onto another interesting point, exceptions are meant to be exceptional (or so the saying goes), while Go's error is par for the course.
    Many developers use exceptions as an 'all hope is lost' - because if it wasn't that type of situation then it wouldn't be exceptional (stacktraces are expensive afterall).
    I've seen many a function written that instead of throwing an exception, would return false, or an error object, as the problem that arose is expected and part of the potential flow.
    From this video, I get the impression that an error is an error, no matter where it comes from in Go's paradigm.

  • @capability-snob
    @capability-snob 5 місяців тому +1

    If you aren't doing manual resource management - e.g. you have defer/with/using/RAII/Context - there's not many reasons to care which function call failed. Where I handle these differently in Go, I'd otherwise use the type to distinguish cases I can handle from those that I can't.
    Well done btw, well done 4:30

  • @z0n_
    @z0n_ 5 місяців тому +10

    I think it really depends what type of software you are building. For example: If you are building a basic CRUD Web API , I would guess +95% of the errors are "unrecoverable". Like if your database connection fails, what are you going to do? If you have bug in your business logic? If you failed to validate the request parameters correctly? For all of these cases there is no "recovering" from them. So having such fine grained error handling makes no sense. You can handle all of these with middleware (single try/catch).
    It's bit different when you are building something where not-recovering isn't really an option, like an operating system, IOT related or something where there are no transactions to save you. It boils down to using the right tool for the right job.

    • @ANONAAAAAAAAA
      @ANONAAAAAAAAA 5 місяців тому +3

      Totally agree.
      Errors in backend are unrecoverable for the most cases.
      The only things can be done are: report or log errors, rollback DB transactions and show users sorry messages.
      It's better to return meaningful messages for user-input-validation errors though.

    • @isodoubIet
      @isodoubIet 5 місяців тому +3

      I think this is pretty much true across the board, regardless of domain. I mean realistically, what strategies are even available to recover from most errors? Trying again?

    • @z0n_
      @z0n_ 5 місяців тому

      @@isodoubIet Yeah, I guess "recovery" is not the correct word here. It's more like generic and non-generic error handling. Most errors can be handled in a generic way but I can imagine situations where application/tool has a lot of internal state and you have to essentially rollback the mutations manually when errors happen. I can see try/catch being really clumsy in those situations.

    • @isodoubIet
      @isodoubIet 5 місяців тому

      @@z0n_ I suspect this is not possible to in TS/JS so this might explain why so many in this audience have issues with exceptions, but in C++ you can design things so that everything rolls back automatically in the case of an exception.
      When an exception is thrown, stacks will unwind and destructors will run. You then have several options for how to deal with rollbacks. You can divide your work in a prepare/commit fashion (so that all possible exceptions are thrown in the "prepare" stage, and all changes in system state happen in the commit stage), or you can have custom types that explicitly roll back changes when destroyed, unless explicitly dismissed. A third option, which may sometimes be possible, is to not rollback completely but to allow things to be in a valid but unspecified state after an exception is thrown.
      Either way you're never rolling things back manually, which is pretty clunky no matter what error handling strategy is being used but surely unworkable in the presence of exceptions.

    • @jeffreybritton3338
      @jeffreybritton3338 5 місяців тому +1

      @@z0n_Or either use a transaction system or defer state modification to the final step of an operation where it can’t fail.

  • @PeterBernardin
    @PeterBernardin Місяць тому

    There's a package called neverthrow that's quite cool, even though it's a bit awkward, it adds that extra layer of security to JS/TS. Takes the mysteries out of errors, and forces you to handle them. To me personally though, although I like the philosophy behind Go's error handling, my issue is that there's no shorthand for the err = ... / err != nil stuff. That pattern is just so common that I just feel it can be shorthanded to like an "or". Something along the lines of `someVal := someFunc() or { handle error }` and the compiler just forces you to handle the error.

  • @mementomori8856
    @mementomori8856 5 місяців тому +4

    I LOVE it personally, just deal with your error right away and be done!

  • @romsthe
    @romsthe 5 місяців тому +1

    Error handling is hard. We have automated coredump analysis and monitoring to make sure it cores in the usual places we don't bother to fix 😅

  • @lukekurlandski7653
    @lukekurlandski7653 5 місяців тому

    I honestly started doing this in Python to some degree when I need more granular control over error handling. Sometimes, I want to handle different ValueErrors differently, and the doing so requires subclassing ValueError with a dozen or so different exception types and its just gets crazy. Way easier to return an error code and read the function’s documentation to figure out what that error code means.

    • @teleraptor6076
      @teleraptor6076 5 місяців тому

      Why not create a single subclass which has the error code as an attribute?

  • @rt1517
    @rt1517 3 місяці тому

    Depending on the context, implicit error management can help you write simpler code, while explicit error management can help you write more robust applications.
    I would argue that it is almost the same with garbage collector and RAII.
    With garbage collector and/or RAII, you can write simpler code. But you are pushed/forced to ignore errors in destructors so your application is not as rigorous as it would be with explicit resource management.

  • @dzisonline
    @dzisonline 5 місяців тому +3

    My biggest issues with errors are not mine but library's. Hunting errors in huge libraries is quite tedious.

  • @ishi_nomi
    @ishi_nomi 5 місяців тому +5

    This totally make sense but is a bit weird though. I mean, the go-style error handling here is just what all language without try-catch simply do. C also do that, just without the tuple-like return syntax.

    • @youtubeenjoyer1743
      @youtubeenjoyer1743 3 місяці тому

      The mistake of the Go design is that Error is a separate thing that needs its own interface. Instead of treating "error" conditions as Errors, you can just treat them as normal conditions.

  • @user-fr5vv3fy8i
    @user-fr5vv3fy8i 5 місяців тому +10

    What if 95% of errors in your app just need to be logged and corresponding http status code returned? It's more more convenient to let those errors bubble up to a centralized error handler instead of repeating err != nil checks all across the call stack.
    The other 5% are usually retries/circuit brakers etc. which are handled by libs (talking about web services in this case)

    • @patrickramos1748
      @patrickramos1748 5 місяців тому +4

      this is the one for me, in most of the code i write, 95% of the time an error happens i just want to rollback the transaction, do some logging, and respond with 500, all of which go in a middleware. that way, i dont have to think about exceptions most of the time

    • @ANONAAAAAAAAA
      @ANONAAAAAAAAA 5 місяців тому +1

      100% agree.
      I also would recommenced to use error monitoring services like Sentry along with logging, they make life a lot easier.

  • @haxguy0
    @haxguy0 5 місяців тому

    Thanks for the interesting perspective

  • @thegittubaba
    @thegittubaba 5 місяців тому

    You have that JS option in golang too, the magic underscore :D

  • @felgenh399
    @felgenh399 5 місяців тому

    Preach
    Never really thought of this. Good content

  • @andreizarra9972
    @andreizarra9972 5 місяців тому

    Oh men I totally agree with you because my code in typescript looks exactly like the example that you do with all those try catches for handle all possible errors, I realized that I'm using the wrong language

    • @xyangst
      @xyangst 5 місяців тому

      Just don't use exceptions at all

  • @programaths
    @programaths 5 місяців тому

    You can also mitigate the issue. I do Apex, and I have objects with embedded error handling. That sounds weird since it breaks the single responsibility principle, but it simplifies the code for the caller.
    You can chain methods using the same objects; each method knows to bail out if you provide an error. This means you can write your implementation and check for errors in one place, handle the error anywhere, and provide an error-free object to continue the chain.
    The API is mostly for noncoders who can't handle errors (they don't even think about it), so it has to be as robust as possible.
    And by avoiding throwing exceptions, it's made very explicit.
    The language doesn't support Generics; otherwise, I would have done a wrapper with overloaded methods accepting those wrappers.

  • @itzhakeretzkdosha6875
    @itzhakeretzkdosha6875 5 місяців тому +5

    Will golang ever go the 'Result' path?
    I'm missing that and union types in GO

    • @OnFireByte
      @OnFireByte 5 місяців тому

      union types might be possible. But for result type, it's certainly not. Imagine everyone have to update their codebase to change from (T, error) to Result[T]

    • @climatechangedoesntbargain9140
      @climatechangedoesntbargain9140 5 місяців тому +1

      ​@@OnFireBytenot everyone has to do that - they're compatible

  • @Sw3d15h_F1s4
    @Sw3d15h_F1s4 5 місяців тому +3

    i think it was Java that has "throws [some type]" for method signatures? so you know exactly what error is thrown and by what? seems like a decent solution, yeah its a bit boilerplate but at least theres a heads up.

    • @phoenix-tt
      @phoenix-tt 5 місяців тому

      It could have been a lint at least. Typescript's had this proposal for a long time, yet it hasn't been merged.

  • @applepie9806
    @applepie9806 5 місяців тому

    Love these kinds of videos, I didn't get the controversy until now

  • @greendsnow
    @greendsnow 5 місяців тому +39

    I wish there were a single liner error handling with Go, though.

    • @samfelton5009
      @samfelton5009 5 місяців тому +2

      What language has single line error handling and what does it look like?

    • @antongorov5275
      @antongorov5275 5 місяців тому +18

      Kinda like "expect" in Rust?

    • @paulooliveiracastro
      @paulooliveiracastro 5 місяців тому +7

      Zig's try/catch are very cool, but the language has to treat errors as something special. In Go errors are simply some value that you return.

    • @andreffrosa
      @andreffrosa 5 місяців тому

      ​@@samfelton5009 C has more or less when you use the return value for errors.
      if(err = f(n)) {...}
      When err is a non-zero int, the if evaluates to true and you deal with the error. The "downside" is that you have to use pointers as out-parameters for setting normal results

    • @notuxnobux
      @notuxnobux 5 місяців тому +12

      @@samfelton5009 zig. In zig instead of "if err != nil return err" you have "try". The try in zig is not exception handling, it just does the go err != nil part automatically. For example:
      try func();
      instead of:
      value, err := func()
      if err != nil {
      return err
      }
      that zig version is the exact same thing as the go version

  • @juice2
    @juice2 5 місяців тому +1

    I really love this design choice in golang

  • @noname78520
    @noname78520 5 місяців тому

    I'll like golang adopts vlang error handling, it is option and result but with simple syntax, I know vlang is knowed as vaporware, but the ideas behind its error handling are pretty good

  • @dranon0o
    @dranon0o 5 місяців тому +1

    ```
    var err error
    if err = something(); err != nil {
    return errors.Join(fmt.Errof("specifics %v", value), err)
    }
    var data Blabla
    if data, err = other(); err != nil {
    return errors.Join(fmt.Errorf("some specifics %v", value), err)
    }
    ```
    You did talked about this sir
    That's how you create wonderful errors management in `go`

  • @mintx1720
    @mintx1720 5 місяців тому +2

    The problem is actually because of the error, you can't use the result immediately in the same line, ever. Even simple things like string to int, getting a value from a map, etc.

  • @gustavcoetzee5018
    @gustavcoetzee5018 4 місяці тому

    Good thoughts thanks

  • @TheMeticulousMoo
    @TheMeticulousMoo 5 місяців тому +1

    Great take. I think JS is starting to move in the direction of Go/Rust with explicit error handling in some new Apis (e.g. Promise.allSettled), but yeah JS error handling kind of sucks rn.

    • @Microphunktv-jb3kj
      @Microphunktv-jb3kj 5 місяців тому +1

      isnt there a new proposal for pipes? :D like in elixir..
      js is the ultimate language :D in that senser, that it picks up all the good stuff from all the languages ,
      from non-js languages, i kind of like Crystal , im surprised its not more popular
      its like ruby, but typed, and c speed with native bindings ,
      and as a noob.. concurrency was more understandable to me in that language vs Go.... and somehow felt powerful writing in thatt language
      since hte language doesnt have braces and brackets all over the place, it felt like you're writing super fast and added illusionary confidence

  • @sbditto85
    @sbditto85 5 місяців тому +3

    It definitely could be better though and it hurts the scan ability of the code as in it can make it harder to understand the non-error handling code flow. Still things could always be worse…

    • @jamesprendergast7158
      @jamesprendergast7158 5 місяців тому

      The 'idiomatic' way of error handling in Go is to try to align the happy path on the left - if you do that then you can configure your IDE to collapse 'if err != nil{ ... }' blocks so that you only see the non-error case. But TBH the happy path is almost always trivially simple - the more interesting and critical parts of your code will probably be the error handling

  • @aazzrwadrf
    @aazzrwadrf 5 місяців тому +8

    The main issue with error handling in Go isn't syntax. It's that the types of errors a function can return are not part of the function signature. You have no idea what errors you're checking for.
    In a nontrivial program, most nontrivial functions end up propagating a union of unknown errors up the stack, which isn't much better than unchecked exceptions.

    • @aazzrwadrf
      @aazzrwadrf 5 місяців тому

      Go isn't Rust. "Errors as values" cannot be implemented well without union types. Having a special syntax for errors would:
      1) Allow union types to be added for errors and not the entire language.
      2) Go, unlike Rust, has recoverable panics, so the runtime is already maintaining a jump table. Having a special syntax for error handling would allow the happy path to have 0 runtime overhead.
      Go should adopt something like Zig's error handling.

    • @isodoubIet
      @isodoubIet 5 місяців тому +1

      @@aazzrwadrf Rust also has recoverable panics, they just as you pretty please not to use it. The machinery for it is there, though.

    • @aazzrwadrf
      @aazzrwadrf 5 місяців тому +1

      ​@@isodoubIetthe process is, but the thread is not recoverable. Therefore the runtime doesn't maintain a jump table to recover from panics.

  • @aaaaanh
    @aaaaanh 5 місяців тому +1

    hmm, Go error handling used to make me suicidal cuz it's a pattern I'm not used to. After 2 weeks of usage, I went back to typescript to build out something else and realized Go handling is way better. Then I learned Rust and everything else was history.

  • @robertlemiesz7143
    @robertlemiesz7143 5 місяців тому

    With the js example you can wrap them in an allSettled

  • @iflux8821
    @iflux8821 5 місяців тому

    Catching someone else’s error is modern GOTO

  • @esser50k
    @esser50k 5 місяців тому +1

    learn to love go error handling.
    It actually makes the code super easy to read, for the happy case you just read whatever is on the left side of the code (the editor often even just hides the return err lines).
    It also makes it super easy to test your functions and make them go into every error case..

  • @rzabcio3
    @rzabcio3 5 місяців тому +1

    I hated Go's err handling. Until I launched my first maybe small but serious app and there were no errors, fatals, boundary cases or anything like that. I just couldn't believe it!
    After 15+ years in IT, one's used to getting unexpected errors, NPEs and similar snafus all the time, especially during first tries. But in Go... NOTHING. At first I thought I screwed up so badly, I missed some errors. But there is the point - it is impossible to miss errors in Go, I correctly managed all situations!
    It was love from the second sight. ❤

  • @w999d
    @w999d 5 місяців тому

    Well, equivalent would be not return Promise, but throwing error anyway (even after special handling)

  • @Veptis
    @Veptis 5 місяців тому

    I have been told that using error handling for flow control is really bad.
    But I now use way too many except Exception as e. If only there was something like with try: or similar.

  • @duke605
    @duke605 5 місяців тому

    Zig imo has the perfect solution to this. If go implemented it it would still feel gomatic but be much cleaner

  • @DemonButt3r
    @DemonButt3r 5 місяців тому +1

    I just wish go had rusts '?' just so i don't have to constantly return something like nil, err in situations where i want the error treated a little higher up

  • @bhargavpandya9189
    @bhargavpandya9189 5 місяців тому

    damn. i thought go error handling was bad since i come from TS background. but now i actually agree with you

  • @Kane0123
    @Kane0123 5 місяців тому

    Bury my head in the sand - agen.

  • @taylorallred6208
    @taylorallred6208 5 місяців тому +8

    One of hardest pills I’ve ever had to swallow: code aesthetics do not matter nearly as much as code pragmatics.

    • @aazzrwadrf
      @aazzrwadrf 5 місяців тому +4

      go's error handling is neither pragmatic nor aesthetic.

    • @isodoubIet
      @isodoubIet 5 місяців тому +1

      Neither matter as much as expressing oneself clearly, though. When you drop a platitude using a made-up expression like "code pragmatics" which people can only guess at the meaning of, you already failed.

  • @germanmalinovsky1719
    @germanmalinovsky1719 5 місяців тому

    In JS you can use Either monad for that case and it's done!

  • @francis_the_cat9549
    @francis_the_cat9549 5 місяців тому +1

    I think youd be interested in ginger bills article on why odin will never have exceptions

    • @marcs9451
      @marcs9451 5 місяців тому

      prime already did an interview with bill but I think the article would be a great read

  • @user-lq4vi5id8l
    @user-lq4vi5id8l 5 місяців тому

    Just add syntactic sugar for this specific usecase. Rust's ? operator is the perfect implementation.
    Putting the Result type in the function header everywhere down the stack does get really repetitive though.

  • @flammea_
    @flammea_ 5 місяців тому +3

    No Joke. Why would I handle a potential error from JSON.parse? What am I supposed to do with the JSON.parse error?

    • @alberto3641
      @alberto3641 4 місяці тому

      no one really knows lol

  • @himanshugupta7313
    @himanshugupta7313 5 місяців тому

    Deterministic exceptions in C++ look cool. Maybe give them a look.

  • @beardedgaminghero3143
    @beardedgaminghero3143 5 місяців тому

    Absolutely fantastic apples to apples and oranges comparison.

  • @ithinkimhipster502
    @ithinkimhipster502 5 місяців тому

    Typescript NEEEDS to add a throws modifier

  • @nickbanderson
    @nickbanderson 5 місяців тому

    *dabs in rust superiority*

  • @ale-cx8vp
    @ale-cx8vp 5 місяців тому

    Amazing video!!

  • @user-sq1ns3td9q
    @user-sq1ns3td9q 26 днів тому

    You can just create some wrap function
    So that it returns undefined rather than throw error

  • @tonyb3123
    @tonyb3123 5 місяців тому +22

    I pretty much never have to read actual stack traces in Go, because I'm hyper dilligent about wrapping _every error_ returned in `fmt.Errorf("some context: %w", err)`. It's genuinely jarring to go back to a Node environment and have to dig through a jumbled stack trace after writing Go and every error is telling me exactly what went wrong in plain english.
    I like to tell people, if you have to dig through an error's stack trace to know what went wrong and where, your error handling is wrong.

    • @thehibbi
      @thehibbi 5 місяців тому

      In principle I like that idea, but what would happen if many concurrent tasks print to stderr at the same time? Does Go have some nice way to handle that? It's a genuine question for a real problem. Rust has the tracing crate for that.

    • @superderpyderps
      @superderpyderps 5 місяців тому +4

      ​@@thehibbi fmt.Errorf isn't a print statement, despite looking like one. It's actually wrapping the error and is then typically returned to the caller. So the answer is, wherever you're actually logging errors will have the full stack for its specific error, meaning if you have a batch of errors, you'll see a "stack" for each error you logged individually. Also, stdlib added some other nice conveniences around the same time, allowing you to aggregate multiple errors into a single error and then be able to unwrap them alongside their individual traces. So you could choose to only log at the top most level even if somewhere in the chain you were aggregating multiple errors

    • @thehibbi
      @thehibbi 5 місяців тому

      @@superderpyderps ah that's good to know. Thanks for the explanation!

  • @sda-jf3cc
    @sda-jf3cc 5 місяців тому +1

    and go does not even force you to handle it, you can just use _ and move on, when you really really need to move fast
    I just do err.ifn and autocomplelete writes the rest for me, and err.pri and again autocomplete writes the rest for me.
    the worst thing in ts is changing your types and including null just because there could be an error, it sucks

  • @Tobsson
    @Tobsson 5 місяців тому +7

    If err := doSomething(); err != nil {
    Handle error
    }
    Is something I find beautiful. Wish it would work even if a value is returned.

  • @danielsan901998
    @danielsan901998 5 місяців тому +10

    With zig you can use try and avoid all that boilerplate.

  • @swikarsharma3118
    @swikarsharma3118 5 місяців тому

    reminded me of papa franku in the end

  • @SkinnyGeek_1010
    @SkinnyGeek_1010 5 місяців тому

    OCaml style Result types in promises can handle this case but it can be painful to wrap every async function

  • @ProjectVastness
    @ProjectVastness 5 місяців тому +1

    I'm still waiting for the experience with F# 😂

  • @mattiasmartens9972
    @mattiasmartens9972 5 місяців тому

    Wow my error handling is so much more explicit now that I’ve rewritten everything in Go! *Turns around* glad there’s no super-easy-to-trigger primitive runtime construct that bypasses all of it.

  • @SandraWantsCoke
    @SandraWantsCoke 5 місяців тому

    I rewrote a small portion of JS code with a Go pattern :D, I've made the function return an array of [error, value] :D. Inside there's a try catch block, but when you use the function, you get these two values in an array and you check if (error !== null ) { handle error } hahaah :D, it is working in production :D

  • @Kpil3
    @Kpil3 5 місяців тому

    I agree that Go is better than JS in this context. However, I think the elixir/erlang approach to errors is a really interesting counter argument to this. Would be interesting to see you explore that

  • @ultraderek
    @ultraderek 5 місяців тому

    I think error handling could happen better. I tried doing it asynchronously but then I didn’t know where and when it did the error.

  • @shellderp
    @shellderp 5 місяців тому +2

    whats nice in Kotlin is you can do val x = try {} catch { return } and not have a var/val that was ever null

    • @copypaste4097
      @copypaste4097 5 місяців тому

      it boggles my mind, that he never has looked into kotlin

  • @burakcankurtarr2751
    @burakcankurtarr2751 5 місяців тому

    awesome video

  • @JeffCaldwell-nemo_omen
    @JeffCaldwell-nemo_omen 5 місяців тому

    Man, I'm just picking up Go as someone who's primarily coded in JS/TS and watching this makes me feel more sane. Up to this point I was just like "Wow, handling errors is such a gigantic pain in the ass." Go may not have ideal error handling, but it's so much less complicated than handling try/catch forks, especially in the lower layers of an app.

  • @philipp04
    @philipp04 5 місяців тому +1

    I've got C++ at university programming classes (I'm studying maths but we do have some programming) and my professor just outright banned exceptions for low performance, weird ass implementation and being wack in general lol. The man is also a CTO of a company which develops software for finding petroleum sources in the ground, the project takes fuckin 20 minutes to build and it has no try catch anywhere in it

    • @romsthe
      @romsthe 5 місяців тому

      And he's right. Status checking like in go is better for perf and maintenance. He will probably tell you to use std::expected now that it's there

    • @isodoubIet
      @isodoubIet 5 місяців тому +1

      "my professor just outright banned exceptions for low performance, weird ass implementation and being wack in general lol."
      He's wrong on all three counts. Exceptions perform better on the happy path, the implementation is just fine, and they're very easy to understand.

    • @Tuniwutzi
      @Tuniwutzi 5 місяців тому +1

      @@isodoubIet Super agree. Especially in C++, where you can avoid writing try/catch in 99% of cases by just using RAII, exceptions are by far the best way. A bit sad that professors are teaching this nonsense when the C++ standard itself encourages the use of exceptions.

    • @isodoubIet
      @isodoubIet 5 місяців тому +1

      @@Tuniwutzi Yeah I can see why someone in a space-constrained environment might want to avoid exceptions, but the rest is largely just FUD.

  • @DmitrijKudriavcev
    @DmitrijKudriavcev 5 місяців тому

    As old-school C programmer, I can tell that this sort of error hanling was in the C forewer. You always return an error code and then you have an if statement to handle this error code. The Go haven't invented anything new here, except now you can separate an actual function response from the error that gives you this nice shugar.
    The main problem with this error handling is precisely a mess of if or switch statements after each function. You need to handle every error and then pass it on to the caller by wrapping it into another error what happens (face it) in 80-90% of the cases.
    But this extra code can be source of all sort of bugs because it is unnecessary operations that language forces you to do after every function call.
    Not only that, but you cannot chain call the functions this way.
    Lets say you have a function that return a string and another one that parses this string and returns integer. Because you must handle errors, you will need to process the first function response then call the second one and process its response as well. While you don't care if the first function didn't return a string or if the string wasn't a valid integer and the second function have failed, you will have to handle error twice and return a nil, err in both cases.
    This is why exeptions in C++ and Java was so great, it eliminates the need of doing that. You can handle errors in one place, you don't need an if statement after every function.
    There is no real problem to find out what function have failed, all you need is to know what type of error have happened and you can achive it by throwing different clases of exceptions. And thanks to inheritance you can group similar exceptions into groups by subclassing them from the some base class and handle this base class. It gives you way more flexibility on how to handle errors in your application and where to handle them.
    I will not be surprised if exceptions will be introduced in go at some point.

  • @egmaleta
    @egmaleta 5 місяців тому

    "monadic" mentioned lets go

  • @brady1123
    @brady1123 5 місяців тому

    The only problem with Go's error-handling is that you're allowed to stuff the `err` result in a bit bucket and ignore it. I wish the compiler would require you to always check the `err` value.

  • @CallousCoder
    @CallousCoder 5 місяців тому

    I always loathed exceptions it’s a sneaky hidden control flow.

  • @seancooper5007
    @seancooper5007 5 місяців тому

    Hear, hear. This man speaks the truth.

  • @alexIVMKD
    @alexIVMKD 5 місяців тому

    That clicking asmr

  • @AlexanderBorshak
    @AlexanderBorshak 5 місяців тому

    That make sense (if you want to know exact error, of course; if not - just one try / catch is enough); but real problem that Go has MORE than one issue in design: aside of this error handling - poor expressiveness, no first-class support for user defined collections (you cannot use for .. range for own types), half-implemented language abstraction (like when append() cannot take care about underline references so developer should bother with reassigning); unsound type system, non-obvious defaults values for some types (nil for map if we use just var, and empty map if we use make()) - and so on. And most of this flaw because authors of Go decide pick up the _simpler_ way of language / compiler implementation; instead of pick up better language's users experience (i.e. developers experience). Each language have strong and weak points. But Go have a bunch of weak ones. Surely, since Go is Turing complete, it can be used for literally anything, but after language with good expressiveness like JS/TS, Python, Rust, even PHP, the Go looks a bit lame...

  • @madskaddie
    @madskaddie 5 місяців тому +1

    this one I'm not with prime. Note that I also prefer return value based errors, but if the model is exceptions, than for the most cases, the error is forwarded to the client (we can discuss if the forward model is correct - vs wrapping the error, but that is other discussion. And in the go example, the err is also forwarded)
    assuming error forwarding is the model, exceptions minimize undefined behavior by nature. period. no one with experience would return null on the catch branch. It is simply wrong. that is for the client to choose, not the implementation
    I prefer return value based because i really like funcional pipelining and function composition. having a try/catch is a mess to compose (the exception behavior is like a non linear, hidden, return value). I like linear control flow. But having that control does make the happy path less clean. choices

  • @1mario999
    @1mario999 5 місяців тому

    Does primagen use optimius manager or nvidia prime ?