Задать вопрос
  • Почему так работают интерфесы в Го?

    Потому что в Го сигнатура метода в интерфейсе должна совпадать полностью. Таким образом на рантайме они быстро матчатся.

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

    Кстати, не рекомендую называть интерфейсы ISomething, это не принято в Го.
    Ответ написан
    Комментировать
  • Как исправить ошибку "cannot download, $GOPATH must not be set to $GOROOT"?

    1. Убедиться, что го установлен не в ~/go и что $GOPATH не смотрит на место его установки
    2. Убедиться, что код проекта находится не в ~/go или вложенных в него папках и не в $GOPATH и вложенных в него папках.
    3. Убедиться, что зависимости в проекте управляются через модули (что сделан go mod init)
    Ответ написан
    Комментировать
  • Как можно повторить на своем сайте 3D-анимацию с чужого сайта?

    1. Разобраться как работает WebGL, как работают вертексные буферы, как работают шейдеры.
    2. Написать на WebGL вертексный шейдер, который интерполирует позицию между координатами из двух буферов. Таким образом можно будет передать в шейдер два буфера и параметр плавного перехода между ними (число между 0 и 1).
    3. Добавить в шейдер управление цветом (на указанном сайте чем точка дальше от камеры, тем она ближе к серому по цвету).
    4. Добавить в шейдер еще один числовой параметр для случайного отклонения точек от своих координат для эффекта "разваливания" модели при действиях мыши.
    5. Собрать весь пайплайн рендеринга, рендеря точки как спрайты с текстурой или же генерируя текстуру кружка во фрагментном шейдере (тут надо посмотреть, что из этого будет быстрее работать).
    6. Написать программу, которая распределит N точек случайным образом внутри нескольких предоставленных моделях и сохранит данные всех расположений точек в файлы, которые будет потом грузить веб-приложение. Можно использовать Blender и геоноды там, у них есть уже готовая функция заполнения модели точками. Главное потом сохранить в таком формате, чтобы было удобно читать в вебе.
    7. Собрать все компоненты вместе, чтобы при скролле и движениях мыши отправлялись нужные параметры в шейдер (две текущие модельки, бленд между ними, случайное отклонение точек, углы камеры)
    Ответ написан
    Комментировать
  • Рисование полигонами поверх полигонов?

    Это можно сделать в любом редакторе, который умеет в ретопологию, например Blender. Включается магнитиком вверху.

    https://www.youtube.com/watch?v=OY7FlJ8xTz0
    Ответ написан
    3 комментария
  • Как настроить пробелы в автоформатировании при арифметических операциях?

    Автоформатирование тут делает не VSCode, а утилита gofmt, VSCode просто ее запускает. Возможности изменить стиль форматирования у gofmt нет, потому что Го по своей идеологии форсит единый стандарт форматирования кода.
    Ответ написан
    Комментировать
  • Golang нужно делать реконнект к дб или поднимать новое соединение?

    Правильная практика — это использовать пул готовых установленных соединений к базе и раскидывать запросы между ними. Почти все библиотеки для работы с БД делают именно так.

    https://go.dev/doc/database/manage-connections
    Ответ написан
    2 комментария
  • Можно ли в структуре указать тип данных отличный от того что лежит в базе данных?

    Хорошей практикой является принимать данные из БД в структуру, которая соответствует по типам тому, что лежит в БД.

    Если вам нужно конвертировать потом эти данные, конвертируйте их в другую структуру, которая расположена на слое бизнес-логики, например.
    Ответ написан
    2 комментария
  • Как под капотом реализованы интерфейсы в go?

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

    При вызове метода гошка на рантайме по таблице находит место в памяти, где этот метод хранится и вызывает его. Причем, первый вызов будет подольше, а следующие вызовы уже быстрее из-за кэширования.
    Ответ написан
    Комментировать
  • Как правильно обрабатывать ошибку в go?

    Для таких случаев в пакете errors есть методы Is и As

    Is проверяет, что указанная ошибка соответствует возвращенной (включая возможность вложенности ошибок)

    As может заполнять структуру ошибки при соответствии типа (тоже включая возможную вложенность)

    https://go.dev/play/p/3j9O079sj97

    Развернуть код
    package main
    
    import (
    	"errors"
    	"fmt"
    )
    
    func main() {
    	err := someFunc()
    	{
    		keyExistsErr := &KeyExistError{}
    		if errors.As(err, keyExistsErr) {
    			fmt.Println("Мы получили ttl:", keyExistsErr.ttl)
    		}
    	}
    }
    
    type KeyExistError struct {
    	ttl string
    }
    
    func (e KeyExistError) Error() string {
    	return fmt.Sprint("Отправить код нельзя раньше чем через ", e.ttl, " сек.")
    }
    
    func someFunc() error {
    	return KeyExistError{
    		ttl: "4",
    	}
    }


    Причем, оно будет работать даже если обернуть ошибку через %w или через errors.Wrap, итд...
    https://go.dev/play/p/x2b_AZKI43t

    Развернуть код

    package main
    
    import (
    	"errors"
    	"fmt"
    )
    
    func main() {
    	err := someFunc()
    	{
    		keyExistsErr := &KeyExistError{}
    		if errors.As(err, keyExistsErr) {
    			fmt.Println("Мы получили ttl:", keyExistsErr.ttl)
    		}
    	}
    }
    
    type KeyExistError struct {
    	ttl string
    }
    
    func (e KeyExistError) Error() string {
    	return fmt.Sprint("Отправить код нельзя раньше чем через ", e.ttl, " сек.")
    }
    
    func someFunc() error {
    	err := someOtherFunc()
    	if err != nil {
    		return fmt.Errorf("error calling someOtherFunc: %w", err)
    	}
    	return nil
    }
    
    func someOtherFunc() error {
    	return KeyExistError{
    		ttl: "4",
    	}
    }

    Ответ написан
  • Как найти утечку памяти?

    В го автоматическое управление памятью, поэтому утечек памяти в классическом смысле быть не может (если не использовать пакет unsafe). Но могут быть утечки горутин (когда вы запускаете горутины, но они не завершаются, а блокируются на каком-то io или мьютексе/канале/...)

    Такое легко ищется с помощью pprof. Добавьте в свою программу веб-интерфейс pprof-а

    Для этого запустите любой http-сервер из стандартной библиотеки и добавьте импорт import _ "net/http/pprof"

    Например так:
    import (
      "net/http"
      _ "net/http/pprof"
    )
    
    ...
    
    func main() {
      ...
      http.ListenAndServe("localhost:8080", nil)
    }


    После этого при запуске программы у вас должен открываться веб-интерфейс pprof-а по адресу 127.0.0.1:8080/debug/pprof

    Дождитесь, когда накопятся утечки и откройте страницу 127.0.0.1:8080/debug/pprof/goroutine?debug=1

    На ней будет список всех работающих горутин и их количество. Найдите группу с самым большим количеством, она и утекает. По стеку посмотрите, где горутина блокируется, тогда поймете, почему они накапливаются.
    Ответ написан
    3 комментария
  • Предложения по оптимизации названий пакетов в Golang?

    Если используете глобальные пакеты (не советую, кстати, лучше dependency injection использовать), то зачем вам там второй уровень вложенности?
    Выводите функции в этих пакетах в корень и тогда будет:
    logger.Setup("log.log")
    database.Connect()
    settings.Setup("options.json")
    Ответ написан
    2 комментария
  • Как решить проблему X does not implement Y при работе с интерфесами?

    По-хорошему, у вас репозиторий не должен никогда возвращать интерфейс. В Го принято возвращать конкретный тип, а не интерфейс. https://github.com/golang/go/wiki/CodeReviewCommen...

    Плюсом еще замечу, что обычно возвращаемый тип является внутренней моделью данных. Это обязанность пакета repository конвертировать из какого-то внушнего представления во внутреннюю модель.

    Если сказать по-другому: в domain-пакетах у вас не должно быть импортов каких-то структур из пакета БД, только наоборот — из пакета БД импортить типы из domain.
    Ответ написан
    Комментировать
  • Как установить пакет в Golang?

    Инструкция у пакета супер-устаревшая. В Го уже давно стандартом считается использование модулей.
    Проекты теперь нельзя класть в GOPATH, нужно использовать другие папки.
    Перед началом работы над проектом вам нужно инициализировать модули через go mod init имя_проекта, именем обычно бывает путь к репозиторию проекта, например: go mod init github.com/myuser/someproject

    После этого можно устанавливать нужные вам зависимости через go get github.com/go-sql-driver/mysql@latest. Вместо latest можно указывать необходимую вам версию пакета.
    Ответ написан
    Комментировать
  • Как скопировать часть сложной формы?

    Такие вещи лучше делать в CAD, а не в Блендере. Но если хочется в Блендере, то вам тут помогут boolean-операции. По описанию я не понял до конца, что хотите сделать, поэтому конкретные операции не подскажу.
    Ответ написан
    Комментировать
  • Как лучше всего замапить запрос в структуру?

    Для слоя базы данных лучше всего иметь отдельные структуры, из которых потом данные перекладывать в слой бизнес-логики. Причем, лучше всего чтобы перекладыванием занимался пакет, ответственный за базу данных.
    Для слоя АПИ тоже лучше свои отдельные структуры, в них перекладывать должен слой АПИ.
    Слой бизнес-логики (модель) должен быть чистым от любых транспортных имплементаций и не импортить никакие пакеты базы или АПИ. Это наоборот, база и АПИ должны импортить в себя структуры бизнес-слоя, чтобы их возвращать и принимать.
    Короче, это я кратко про букву D в слове SOLID.
    Ответ написан
    2 комментария
  • Что делать если картинка полностью не рендерится?

    Возможно, выбран регион картинки для рендера? Нажмите ctrl+alt+b, чтобы его сбросить.
    Ответ написан
    Комментировать
  • Как добавить к концу строки \r?

    Так как либа для записи требует интерфейс io.Writer, вы легко можете создать свой прокси-райтер, который будет при встрече каждого \n добавлять еще и \r

    В самой примитивной реализации это будет выглядеть так:
    package main
    
    import (
    	"bytes"
    	"io"
    	"os"
    
    	"github.com/olekukonko/tablewriter"
    )
    
    func main() {
    	data := [][]string{
    		{"1/1/2014", "Domain name", "2233", "$10.98"},
    		{"1/1/2014", "January Hosting", "2233", "$54.95"},
    		{"1/4/2014", "February Hosting", "2233", "$51.00"},
    		{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
    	}
    
    	w := New(os.Stdout)
    
    	table := tablewriter.NewWriter(w)
    	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
    	table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
    	table.SetBorder(false)                                // Set Border to false
    	table.AppendBulk(data)                                // Add Bulk Data
    	table.Render()
    }
    
    type ClrfAdder struct {
    	writer io.Writer
    }
    
    func New(w io.Writer) *ClrfAdder {
    	return &ClrfAdder{
    		writer: w,
    	}
    }
    
    func (c ClrfAdder) Write(p []byte) (n int, err error) {
    	replaced := bytes.ReplaceAll(p, []byte("\n"), []byte("\r\n"))
    	n, err = c.writer.Write(replaced)
    	if err != nil {
    		if n > len(p) {
    			n = len(p)
    		}
    		return n, err
    	}
    	return len(p), nil
    }


    Но можно использовать готовые стрим-реплейсеры, например https://github.com/icholy/replace
    Ответ написан
  • Как сделать болле корректно даный цикл?

    Вполне нормальное у вас решение. Разве что можно сделать так:
    func divide(x ...float64) float64 {
      if len(x) == 0 {
        return 0
      }
      if len(x) == 1 {
        return x[0]
      }
      a := x[0]
      for _, v := range x[1:] {
        a /= v
      }
      return a
    }
    Ответ написан
    1 комментарий
  • Можно ли изменять неимпортируемые поля структуры в других пакетах программы?

    Не понял, в чем проблема. Есть у вас пакет cache, внутри тип Cache и функция New

    Так как функция New экспортируемая, вызываете ее из любого пакета и создаете себе экземпляр кэша. Потом этот экземпляр через dependency injection кладете во все нужные вам экземпляры сервисов.
    Ответ написан
    Комментировать