В этой статье я покажу, почему легче писать служебные функции/методы, используя Generic в Golang. Сама функция полезности — это функция, которая выполняет обычное поведение и может быть повторно использована в нескольких местах.

Универсальное программирование – это стиль компьютерного программирования, в котором алгоритмы записываются в терминах типов, которые должны быть определены позже, а затем при необходимости создаются экземпляры для конкретных типов, предоставляемых в качестве параметров.

Используя generic в Go, мы можем иметь кодовую базу с меньшим количеством дубликатов, потому что в моей кодовой базе было несколько дубликатов до того, как я использовал Generic. Особенно кодовая база, использующая Go версии 1.17 и ниже.

«Разговоры дешевы. Покажи мне код». ― Линус Торвальдс

Давайте взглянем на пример кода ниже.

func InArray(haystack []string, needle string) bool {
  for _, hay := range haystack {
    if hay == needle {
       return true
    }
  }
  return false
}

Функция InArray предназначена для проверки существования элемента в массиве. На практике мне нужно дублировать функцию, если я проверяю массивы с другим типом данных.

func InArrayString(haystack []string, needle string) bool {
  for _, hay := range haystack {
    if hay == needle {
       return true
    }
  }
  return false
}

func InArrayInt(haystack []int, needle int) bool {
  for _, hay := range haystack {
    if hay == needle {
      return true
    }
  }
  return false
}

func InArrayFloat(haystack []float64, needle float64) bool {
  for _, hay := range haystack {
    if hay == needle {
      return true
    }
  }
  return false
}

Если мы хотим удалить дублирование кода из кодовой базы без использования универсального, мы можем использовать библиотеку reflect, особенно если кодовая база по-прежнему использует Golang версии 1.17 или ниже.

func InArray(haystack any, needle any) (bool, error) {
  haystackValue := reflect.ValueOf(haystack)
  if haystackValue.Kind() != reflect.Slice && haystackValue.Kind() != reflect.Array {
     return false, errors.New("haystack is not an array")
  }

  needleValue := reflect.ValueOf(needle)
  for i := 0; i < haystackValue.Len(); i++ {
     val := haystackValue.Index(i)
     if val.Kind() == needleValue.Kind() && val.Interface() == needleValue.Interface() {
       return true, nil
     }
  }

  return false, nil
}

Использование reflect может удалить дублирование в коде, но увеличит сложность функции. Но из-за затрат мы каким-то образом теряем возможность «статической типизации», потому что она должна проверять тип данных во время выполнения. Кроме того, нам нужно убедиться, что предоставленный ввод действителен, и обрабатывать ошибки при использовании функции.

Если вы все же хотите избежать усложнения кода, вы можете сначала преобразовать массив, а затем передать его служебной функции.

// Declaration
func InArray(haystack []string, needle string) bool {
  for _, hay := range haystack {
    if hay == needle {
       return true
    }
  }
  return false
}


func main() {
  
  // Usage
  haystack := []int{1, 2, 3, 4, 5}
  needle := 1

  haystackString := make([]string, 0, len(haystack))
  for _, hay := range haystack {
     haystackString = append(haystackString, string(hay))
  }

  fmt.Println(InArray(haystackString, string(needle)))
}

При преобразовании вам необходимо преобразовывать массив каждый раз, когда тип массива отличается от аргумента InArray. Другими словами, при использовании этой служебной функции у вас все еще будет повторяющийся код. Кроме того, преобразование усложняет ваш код (временная сложность в приведенном выше коде).

Мы можем удалить дублирование и по-прежнему иметь простой код, используя Generic.

func InArray[T comparable](haystack []T, needle T) bool {
  for _, hay := range haystack {
    if hay == needle {
      return true
    }
  }
  return false
}

Код с универсальным и без универсальным сначала похож, и вам не нужно добавлять больше сложности, как при использовании библиотеки reflect.

Generic подходит для создания функции полезности с более чем одним типом ввода. Но не злоупотребляйте этим.

Хорошо, это все. Также можно поиграть с кодом здесь. Спасибо.