elfuegobiz
@elfuegobiz

Зачем нужны дженерики, если можно проще?

Кто в теме, объясните, пожалуйста,почему в golang нельзя []что-то привести к []interface{} :
func remove(slice []interface{}, s interface{}) []interface{} {
    return append(slice[:s], slice[s+1:]...)
}

a := []string{"a", "b", "c"}
b := []int{1, 2, 3}

a = remove(a, 2)
b = remove(b, 1)


Вот -- более широкий пример. Если раскомментировать последние 2 строки в main(), то программа просто не соберётся. Вопрос: почему?? Ведь я могу передать переменную типа i1 в параметры типа intf, почему нельзя передать []i1 в параметр []intf? В смысле, почему не сделать, чтобы так было можно. Так бы было намного проще в том смысле, что вопрос с дженериками не стоял бы так остро.

package main

import (
	"fmt"
	"strconv"
)

type intf interface {
	get() string
}

type i1 string

func (s i1) get() string {
	return string(s)
}

type i2 int

func (s i2) get() string {
	return strconv.Itoa(int(s))
}

func print1(a []intf) {
	for _, i := range a {
		fmt.Print(i, "; ")
	}
}

func main() {
	a := []intf{i1("xyz"), i2(42)}
	print1(a)
	//b := []i1{i1("xyz")}
	//print1(b)
}
  • Вопрос задан
  • 246 просмотров
Решения вопроса 1
tumbler
@tumbler
бекенд-разработчик на python
В Go отстойная алгебра интерфейсов. Если структура A реализует интерфейс I, это не значит, что []A реализует []I.
А всё потому что приведение типов к интерфейсу - это манипуляции с идентификатором типа в структуре. Очевидно, нужно в цикле заменить этот идентификатор в каждом элементе слайса, чтобы достичь нужного эффекта. Хотелось бы, чтобы это инлайнилось или как-то при компиляции автоматом обрабатывалось, но вот пока нет.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
axifive
@axifive
Software Engineer
Кто в теме, объясните, пожалуйста, почему в golang нельзя []что-то привести к []interface{} :

Привести можно, но это необходимо сделать самостоятельно. interface{} это отдельный тип и slice []interface{} может принять только срез этого типа.

func remove(slice []interface{}, s interface{}) []interface{} {
    return append(slice[:s.(int)], slice[s.(int)+1:]...)
}

func main() {
  a := []string{"a", "b", "c"}
  // преобразование
  ai := make([]interface{}, len(a))
  for i, v := range a {
    ai[i] = v
  }
  ai = remove(ai, 2)

  b := []int{1, 2, 3}
  // преобразование
  bi := make([]interface{}, len(b))
  for i, v := range b {
    bi[i] = v
  }
  bi = remove(bi, 1)
        
  fmt.Printf("%T: %v\n", ai, ai)
  fmt.Printf("%T: %v\n", bi, bi)
}

https://play.golang.org/p/gRHqO31LIJU

В смысле, почему не сделать, чтобы так было можно.

Потому что s []string это массив массивов типа byte, а i []int массив чисел типа int и они имеют в памяти разное представление.

Такая же история:
func main() {
  b := []i1{i1("xyz")}
  bi := make([]intf, len(b))
  for i, v := range b {
     bi[i] = v
  }
  print1(bi)
}

https://play.golang.org/p/z9BoM5lutV0
Ответ написан
Комментировать
index0h
@index0h
PHP, Golang. https://github.com/index0h
Зачем нужны дженерики, если можно проще?

дженерики тут ни при чем.

По сути вы пытаетесь передать в print1 (в заккоментированном коде) срез другого типа. Да, тип элементов среза отвечает вашему интерфейсу intf, но сам срез нет. Следующий вызов вполне валиден, но с типам i1 и i2 вы такого не провернете.
func main() {
  print1([]intf{nil, nil})
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы