1. 28
  1. 13

    I’ve had race conditions, problems with forgetting to close channels, issues with the verbosity of converting between types, stress with refactoring packages and import paths, vendoring issues and many other things, but panic: runtime error: invalid memory address or nil pointer dereference is one that I can’t really remember ever having seen in Go.

    I just don’t use “nil” for anything but empty error values.

    1. 2

      The typecast example could be expanded;

      if myNumber == nil {
          // handle nil
      }
      number := numberPointer.(*myNumber)
      

      I would prefer

      number, ok := numberPointer.(*myNumber)
      

      Though I’m not sure how to syntactically handle it so there is no room for error…

      1. 1

        You might be interested in sgo, a “Go dialect” that prevents nil errors by enforcing if thing != nil checks at compile time: https://github.com/tcard/sgo/blob/master/README.md

        1. 0

          So far I’ve only found one solution that is actually robust. Which is to manually check that the value is not nil before actually using it.

          This seems reasonable to me. If anything, I’d consider knowing how and when to use this kind of check a part of language competency knowledge as it is how Go was designed.

          1. 9

            We expect people to be competent enough to not crash their cars, but we still put seatbelts in.

            That’s perhaps a bad analogy, because most people would say that there are scenarios where you being involved in a car crash wasn’t your fault. (My former driver’s ed teacher would disagree, but that’s another post.) However, the point remains that mistakes happen, and can remain undiscovered for a disturbingly long period of time. Putting it all down to competence is counter to what we’ve learned about what happens with software projects, whether we want it to happen or not.

            1. 9

              I wish more languages had patterns. Haskell example:

              data Named = Named {Name :: Text} deriving Show
              
              greeting :: Maybe Named -> Text
              greeting (Just thing) = "Hello " + (Name thing)
              greeting _ = ""
              

              You still have to implement each pattern, but it’s so much easier, especially since the compiler will warn you when you miss one.

              1. 3

                Swift does this well with Optionals

                1. 5

                  You can even use an optional type in C++. It’s been a part of the Boost library for a while and was added to the language itself in C++17.

                  1. 4

                    You can do anything in C++ but most libraries and people don’t. The point is to make these features integral.

                    1. 1

                      It’s in the standard library now so I think it’s integral.

                      1. 4

                        If it’s not returned as a rule and not as an exception throughout the standard library it doesn’t matter though. C++, both the stdlib and the wider ecosystem, rely primarily on error handling outside of the type-system, as do many languages with even more integrated Maybe types

                  2. 2

                    Yep. Swift has nil, and by default no type can hold a nil. You have to annotate them with ? (or ! if you just don’t care, see below).

                    var x: Int = nil // error
                    var x: Int? = nil // ok
                    

                    It’s unwrapped with either if let or guard let

                    if let unwrapped_x = x {
                        print("x is \(x)") 
                    } else {
                        print("x was nil")
                    }
                    
                    guard let unwrapped_x = x else {
                        print("x was nil")
                        return
                    }
                    

                    Guard expects that you leave the surrounding block if the check fails.

                    You can also force the unwraps with !.

                    let x_str = "3"
                    let x = Int(x_str)! // would crash at run-time if the conversion wouldn't succeed
                    

                    Then there’s implicit unwraps, which are pretty much like Java objects in the sense that if the object is nil when you try to use it, you get a run-time crash.

                    let x: Int! = nil
                    
                2. 7

                  Hey, I’m the author of the post. And indeed that does work, which is why I’m doing that currently. However, like I try to explain further in the post this has quite some downsides. The main one is that it can be easily forgotten. The worst part of which is that if you did forget, you will likely find out only by a runtime panic. Which if you have some bad luck will occur in production. The point I try to make is that it would be nice to have this be a compile time failure.

                  1. 1

                    Sure, and that point came across. I think you’d agree that language shortcomings - and certainly this one - are generally excused (by the language itself) by what I mentioned?