• taladar@sh.itjust.works
    link
    fedilink
    arrow-up
    4
    ·
    10 months ago

    What I had in mind when talking about that standard library thing was one case in particular that I had found where someone had to implement deduplication of elements in a vector/array/list (or whatever Go called it, don’t remember that bit) locally because Go does not support function generic over the type of container element.

    And the whole if err != nil { return err } bit is a huge part of what makes Go code unreadable. I have also found at least half a dozen bugs related to that construct where people just did not print any of the relevant information in error cases because of the lazy copy&paste of that construct in cases I had to debug.

    • sugar_in_your_tea@sh.itjust.works
      link
      fedilink
      arrow-up
      1
      ·
      edit-2
      10 months ago

      deduplication

      The best solution here is a map, using keys as the set. So something like:

      func dedup(arr []T) (ret []T) {
          m := make(map[T]bool)
          for _, t := range T {
              m[t] = true
          }
          
          // optimization: make ret the right size
          for t := range m {
              ret = append(ret, t)
          }
      }
      

      I haven’t used Go’s new generics, but I’m guessing this would work fine, provided T is a value type (or can be converted to one). If you know you need deduplication at the start, just use a map at the start.

      If you don’t have value types, you’re going to have a hard time regardless of language (would probably need some OOP features, which adds a ton of complexity). But you can get pretty far with that pattern. If you add a Hash() int to your type:

      func dedup(are []T) (ret []T) {
          m := make(map[int]bool)
          for _, t := range arr {
              h := t.Hash()
              if !m[h] {
                  m[h] = true
                  ret = append(ret, t)
              }
          }
      }
      

      err… people just did not print any of the relevant information in error cases

      That’s what error wrapping is for:

      if err != nil {
          return fmt.Errorf("context: %w", err)
      }
      

      This makes it so you can use the errors package to unwrap errors or check if an error is a given type. Or you can propagate it like I’ve shown above.

      So I see this as programmer error. Rust has the same issue since it’s easy to just throw a ? in there and bail early without additional context. The simpler form is easier to catch in Go vs Rust in a code review because it’s more verbose.

      It seems you don’t like verbosity, which is fair. I’m fine with verbosity, I don’t like surprises, and Go has enough of those that I generally avoid it.