Ответы пользователя по тегу Go
  • Почему env GOPATH больше не имеет значения?

    Раньше все зависимости лежали в GOPATH, но это было неудобно тем, что не было никакого управления этими зависимостями. Нельзя было зафиксировать их конкретные версии и нельзя было для разных проектов сделать разные зависимости, потому что все было в одной центральной папке.
    Потом в го ввели систему модулей:
    https://go.dev/blog/using-go-modules
    https://habr.com/ru/company/otus/blog/503918/

    Теперь в корневой папке каждого проекта есть файл go.mod, который перечисляет зависимости этого проекта и их конкретные версии. Поэтому необходимость в GOPATH отпала, теперь Go при сборке использует модули, указанные в go.mod и не ходит в GOPATH вообще, это намного удобнее.
    Ответ написан
    4 комментария
  • Как зашифровать http запрос?

    Использовать https, тогда можно будет зашифровать в том числе урл.
    Если делать свое шифрование поверх http, то во-первых, это будет сильно сложнее сделать правильно и надежно, во-вторых, не получится зашифровать урл, потому что по стандарту http он должен быть в открытом виде.
    Ответ написан
    Комментировать
  • Как понять от первого элемента в слайсе(capacity)?

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

    То, что в канал данные пишутся медленнее, чем читаются, не должно вызывать никаких проблем при правильном подходе. У вас просто очень странно написан воркер, он на каждое событие запускает горутину и почему-то останавливается, если буфер канала пустой. Воркера надо по-хорошему останавливать, когда канал закрыт, а не пуст.

    Лучше сделать так
    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    	"sync"
    )
    
    func main() {
    	urls := make(chan string)
    	go fillChannel(urls)
    
    	// создаем группу для ожидания, того, что все воркеры завершены
    	wg := &sync.WaitGroup{}
    
    	for i := 0; i < 5; i++ {
    		// при запуске каждого воркера, увеличиваем счетчик в группе на 1
    		wg.Add(1)
    		go requestWorker(urls, wg)
    	}
    
    	// ждем, пока счетчик в группе не будет равен 0
    	wg.Wait()
    }
    
    func requestWorker(channel <-chan string, wg *sync.WaitGroup) {
    	// По завершении воркера счетчик в группе будет уменьшен на 1
    	defer wg.Done()
    	// Заодно пишем сообщение о завершении воркера
    	defer println("Worker stopped")
    
    	// Постоянно читаем из канала новые сообщения
    	// цикл автоматически завершится, когда канал закроется и буфер будет пуст
    	for url := range channel {
    		println(url)
    	}
    }
    
    func fillChannel(channel chan<- string) {
    	file, err := os.Open("data.txt")
    	defer file.Close()
    
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    
    	fileScanner := bufio.NewScanner(file)
    	fileScanner.Split(bufio.ScanLines)
    
    	for fileScanner.Scan() {
    		channel <- fileScanner.Text()
    	}
    
    	// закрываем канал, когда данные кончились
    	// в го принято, чтобы канал закрывал только тот, кто в него пишет
    	close(channel)
    }



    Этот паттерн называется worker pool. Мы пишем в канал все нужные данные и закрываем канал, когда данные кончились. Благодаря тому, что воркеры читают из канала через range, цикл просто выходит, когда канал закрыт и воркеры завершаются.
    WaitGroup используется для того, чтобы подождать, пока воркеры доработают последние данные.
    Ответ написан
    Комментировать
  • Как в процессе выполнения программы считывать вывод?

    Все гораздо проще. Вы можете сразу в stdout подложить буфер и смотреть в него при необходимости
    cmd := exec.Command("python", "script.py")
    stdoutBuf := bytes.NewBuffer(nil)
    cmd.Stdout = stdoutBuf
    if err := cmd.Run(); err != nil {
    	log.Println(err.Error())
    }
    
    fmt.Println(stdoutBuf.String())


    Или еще проще, если вам нужны и stdout и stderr вместе:
    cmd := exec.Command("python", "script.py")
    res, err := cmd.CombinedOutput()
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Println(string(res))
    Ответ написан
    3 комментария
  • Что не так с потоками?

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

    https://go.dev/play/p/-aB5SS1amav
    package main
    
    import (
    	"fmt"
    	"log"
    )
    
    func main() {
    	code := map[string]interface{}{
    		"test": "test",
    	}
    
    	// извлекаем interface{} из мапы внутри которого строка
    	ifaceStr := code["test"]
    
    	// извлекаем строку из interface{}
    	str, ok := ifaceStr.(string)
    	if !ok {
    		log.Fatal("it's not a string")
    	}
    	fmt.Println(str)
    }


    Но почему бы вам не делать анмаршал в map[string]string сразу? Чтобы не конвертить лишний раз?
    Ответ написан
  • Можно ли использовать значение переменной как название структуры?

    Так делать не стоит, в го это решается другими способами. Вы пытаетесь переносить подход из скриптовых языков в го, так это не работает.
    Ответ написан
  • Как отдавать статику через julienschmidt/httprouter?

    r.Handler("GET", "/", fServer)
    Этот шаблон значит полное совпадение с корнем, поэтому у вас 404 на все вложенные страницы. Чтобы этого избежать сделайте так:

    r.Handler("GET", "/*filepath", fServer)
    Ответ написан
    3 комментария
  • Golang выдает ошибку, что делать?

    Вам компилятор четко пишет
    main redeclared in this block {строка 8 столбец 6 }
    previous declaration at ./hello.go:18:6

    Перевожу: функция main написана несколько раз, второй раз она встречается в файле hello.go на 18 строчке.
    Видимо, у вас в одной папке несколько файлов и в нескольких из них есть функция main. Так делать нельзя, все, что находится в одной папке, го считает одним пакетом.

    Что касается второй ошибки, отправляйтесь изучать основы синтаксиса языка, иначе далеко так не уедете. У вас лишние запятые в конце списка параметров и в конце списка переменных.
    Ответ написан
    9 комментариев
  • Запутался с установкой golang, что делать?

    потом я хотел настроить перменную GOPATH, а там как я понял надо указать путь до ГО и там выбрать папку scr.


    Нет, вы пользуетесь каким-то странный старым руководством. Если из пакетов ставите, то у вас уже всё нужное есть. Никакой GOPATH уже давно прописывать не надо.
    Ответ написан
    Комментировать
  • Как через os/exec запустить сторонний exe файл в GoLang?

    При использовании exec.Command, первым аргументом там всегда исполняемый файл, а дальше идет перечисление аргументов, которые этому исполняемому файлу будут передаваться. Причем, их не надо экранировать.

    Несколько примеров:
    path := `c:\Program files\some program\program.exe`
    // аналогично вызову в консоли start "c:\Program files\some data"
    cmd := exec.Command("start", path)
    ......
    
    // аналогично вызову в консоли "c:\Program files\some program\program.exe"
    cmd := exec.Command(path)
    ......
    
    // аналогично вызову в консоли some_prog.exe -flag1 value1 -flag2 value2
    cmd := exec.Command("some_prog.exe", "-flag1", "value1", "-flag2", "value2")
    ......


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

    Кстати, если вам просто нужен полный ответ команды stdout+stderr, можете использовать метод cmd.CombinedOutput(). Он выполнил программу, дождется ее завершения и вернет полностью то, что она писала в консоль.
    Ответ написан
    7 комментариев
  • Как вы решаете проблему циклических импортов?

    orders и payments должны лежать внутри одной модели.
    Если кода много, то можете вынести эти структуры в отдельный пакет, посвященный только внутренним структурам данных, это и будет та самая одна модель.
    пакет structures: типы orders и payments
    пакет orders: импортирует structures
    пакет payments: импортирует structures

    А в целом, в го связанность кода (и кольцевые импорты вместе с ней) принято разрушать через использование интерфейсов.
    У вас в идеале должны быть пакеты с моделями, в которых должны лежать внутренние типы данных и объявлены интерфейсы к внешним источникам типа баз данных, итд.
    Внутри пакета базы данных у вас должны импортиться структуры данных из модели и производиться конверсия сырых данных в структуры модели. А сама база данных в модели объявлена через интерфейс, поэтому пакет базы не приходится импортить.
    Такая схема дает еще плюсы, что легко тестить модель, подменяя базу моками.
    Ответ написан
    Комментировать
  • Как получить базовый массив слайса или ссылку на него?

    Никак не получить, это и не нужно в нормальной ситуации
    Если очень хочется, то можно это сделать через unsafe. Но используя пакет unsafe вы лишаетесь безопасности по памяти.
    Ответ написан
    Комментировать
  • Как провести итерацию функций в map?

    Старайтесь никогда не использовать пустой интерфейс (interface{}), только в самых крайних случаях.
    Тут вам нужно просто изменить тип мапы на map[string][]func(string, interface{})

    Не знаю, почему shortName у вас тоже имеет тип interface{}, его бы изменить на конкретный тип или конкретный интерфейс. Тогда код станет еще понятнее и удобнее.
    Ответ написан
    Комментировать
  • Как ускорить работу кода?

    Для таких целей отлично подходит паттерн "worker pool"
    https://gobyexample.com/worker-pools

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

    Проверьте, что хост и порт постгреса правильные.
    Если запускаете в докере, то учтите, что коннект к localhost там не работает, потому что у каждого контейнера свой локалхост.
    Ответ написан
    Комментировать
  • Как правильно определить какой JSON вернулся?

    Успешность обычно проверяют по http-коду ответа.

    Если же сервис сделан так, что код ответа всегда 200, то единственный верный способ будет пытаться анмаршалить оба и смотреть на поля, всё верно.
    Ответ написан
    1 комментарий
  • Почему int при делении int на int с остатком?

    Это не деление с остатком, это целочисленное деление.
    Если делишь одно целое число на другое целое число, работает целочисленная арифметика, потому что го это строго статически типизированный язык.
    Чтобы получить дробное число, нужно явно делить одно число с плавающей точкой на другое.
    package main
    
    import "fmt"
    
    func main() {
    	var some = float64(5) / float64(3)
    	fmt.Println(some)
    	fmt.Printf("%T", some)
    }


    Происходит это потому что компьютер так работает на уровне железа. Если процессору дать команду на деление двух целых, ответом будет целое. Если дать команду на деление двух флоатов — будет флоат.
    В некоторых языках компилятор или интерпретатор берут на себя смелость решать за пользователя, какой ему тип нужен и автоматически включают нужный тип деления в зависимости от ситуации (обычно включается флоат на флоат, как в JS, например). Но это может приводить к ошибкам в некоторых ситуациях.
    Поэтому в го, C, C++ и еще многих других языках (обычно это статически типизированные языки), пользователь должен явно выбирать требуемый тип деления, используя нужные типы переменных.
    Ответ написан
    1 комментарий
  • Go. Что делать, если в коде много схожих друг с другом функций?

    Наоборот, написать общую функцию для таких целей — это хороший способ.
    У вас тут посыл запроса, обработка кода ответа, парсинг. Это всё отлично выносится в общую функцию, которой потом удобно будет пользоваться. Особенно с дженериками, которые добавили в go1.18
    Ответ написан
    2 комментария