Ответы пользователя по тегу Go
  • Как правильно в go структуру из базы в шаблон html в gin?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Итерировать нужно слайс, а не структуру, т.е. []Users.
    Вот работающий пример.
    rows, err := conn.Queryx("SELECT id, name FROM users")
    if err != nil {
          log.Fatalln(err)
    }
    defer rows.Close()
    
    users := []User{}
    
    for rows.Next() {
        if rows.StructScan(&user) != nil {
          log.Fatalln(err)
        }
        users = append(users, user)
    }
    
    ctx.HTML(http.StatusOK, "users/index.tmpl",  gin.H{"values": users})
    Ответ написан
    Комментировать
  • Почему крашится программа в linux?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    обработайте ошибку Exec, скорее всего там ошибка, а res в этом случае nil, по этому и panic
    res, err := p.Exec(js)
    if err != nil {
       log.Panic(err)
    }
    Ответ написан
    4 комментария
  • Почему анонимная функция работает только с последним значением переменной?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Тут суть в том, что в момент, когда анонимная функция начнёт выполняться - выполнение цикла к тому моменту уже закончится и i будет равна 10.
    Т.е. шедулер не успевает запустить горутины, они фактически запускаются уже после цикла, но часть из них может успеть и во время цикла запуститься...

    Самый простой способ получить желаемый результат передать значение как параметр
    go func(idx int) {
        ...
    }(i)


    Полный пример:
    package main
    
    import (
        "sync"
    )
    
    const N = 10
    
    func main() {
        m := make(map[int]int)
        wg := &sync.WaitGroup{}
        mu := &sync.Mutex{}
        wg.Add(N)
        for i := 0; i < N; i++ {
            k := 5
            go func(idx int) {
                defer wg.Done()
                mu.Lock()
                println(idx, k)
                m[idx] = k
                mu.Unlock()
            }(i)
        }
        wg.Wait()
        println(len(m))
        println(m[0])
    }
    Ответ написан
    4 комментария
  • Чему равна переменная r после второго вызова panic?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Выполнение функции f() завершается после первого panic, потом отрабатывает defer.
    Второй panic выполнен не будет, т.к. выполнение функции уже завершилось и до него очередь не дойдёт.
    Можно добавить немного log.Println в ваш пример и проследить ход выполнения, например вот так
    package main
    
    import "log"
    
    func f() {
      defer func() {
        if r := recover(); r != nil {
          log.Printf("recover:%#v", r)
        }
      }()
      log.Println("Start first panic")
      panic(1)
      log.Println("After first panic")
      panic(2)
      log.Println("After second panic")
    }
    
    func main() {
      f()
      log.Println("After f()")
    }

    Вывод будет таким
    2020/07/20 23:14:07 Start first panic
    2020/07/20 23:14:07 recover:1
    2020/07/20 23:14:07 After f()
    Ответ написан
    4 комментария
  • Почему одна функция возвращает true, а вторая false?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Потому, что функция InitEfaceType фактически возвращает структуру, а не указатель.
    Структура не может быть nil. Если вы возвращаете указатель на структуру - тогда уже на nil можно проверить.

    Сделайте fmt.Printf("Type: %T\n", InitEfaceType()) и вы увидите тип "main.S"

    Чтобы иметь возможность проверить интерфейс на nil можете сделать вот так:
    func InitEfaceType() interface{} {
      var s *S
      return s
    }
    ...
    fmt.Println(InitEfaceType() == (*S)(nil))

    полный код

    package main

    import (
    "fmt"
    )
    type S struct{}

    func (s S) F() {}

    func InitPointer() *S {
    var s *S
    return s
    }

    func InitEfaceType() interface{} {
    var s *S
    return s
    }

    func InitType() S {
    var s S
    return s
    }

    func main() {
    fmt.Println(InitPointer() == nil)
    fmt.Println(InitEfaceType() == (*S)(nil))
    //fmt.Println(InitType() == nil)
    }

    Ответ написан
    5 комментариев
  • Почему в результате выводятся различные адреса?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Это происходит потому, что вы храните значения, а не указатели и, соответственно, вот тут for i, a := range t.array в `a` - создаётся копия value.
    Чтобы адреса были одинаковые нужно хранить указатели, а не значения, т.е. нужно делать вот так
    type Test struct {
    	array []*Array
    }

    Поправил ваш код в песочнице, теперь адреса одинаковые
    Ответ написан
    4 комментария
  • Как вывести всю HTML страницу в go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    У вас проблема в том, что не отображается статика (изображения, CSS).
    Эту проблему можно решить, как минимум, двумя способами.
    Обслуживать статику вебсервером, например Nginx.
    Или сделать handler на Go, который будет отдавать статику.
    Вот тут есть хороший пример как это можно сделать

    https://www.alexedwards.net/blog/serving-static-si...

    Если кратко, как вариант:
    1. создайте папку static и переложите туда файлы `style.css`, `dog2.jpg`
    2. добавьте в URL у картинок и CSS "/static/", чтобы было вот так
    <link rel="stylesheet" href="/static/style.css"/>
    <img src="/static/sob2.jpg" />

    3. сделайте обработчик /static
    // добавьте эти строки
    fs := http.FileServer(http.Dir("./static"))
    http.Handle("/static/", http.StripPrefix("/static/", fs))
    // перед этой строкой
    http.HandleFunc("/", viewHandler)


    Если настраивать через Nginx, тогда минимальные рабочие настройки будут примерно такими
    server {
        listen *:80;
    
        server_name localhost;
    
        # root /тут_путь_к_вашей_папке/static;
        # для Windows будет что-то типа такого
        root D:/GoExam/static;
    
        location / {
            try_files $uri @process;
        }
    
        location @process {
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_send_timeout 30;
            proxy_read_timeout 30;
        }
    }

    добавлять их нужно в файл nginx.conf, в секцию http
    пример

    http {
      # тут идут настройки Nginx
      ....
      # тут дописываете код, что я выше написал, грубо перед закрывающей "}"
    }

    Ответ написан
  • Почему ошибка в выводе (golang)?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Он не выводит %
    % - это скорее всего уже ваш шел выводит.
    Добавьте код перевода строки "\n" в конец вот так fmt.Printf ("Hello %v\n", x) и будет то, что вы хотите
    Ответ написан
    Комментировать
  • Как сделать два пула соединений (с разными БД)?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Именно так, как вы и написали
    db1, err := sql.Open("mysql", DSN1) // Сначала к одной БД подключение.
    db2, err := sql.Open("mysql", DSN2) // Ниже к другой БД подключение.
    
    // настраиваете пулы как вам нужно
    db1.SetMaxIdleConns(10)
    db1.SetMaxOpenConns(100)
    
    db2.SetMaxIdleConns(10)
    db2.SetMaxOpenConns(100)
    Полный пример кода

    package main
    
    import (
        "database/sql"
        "log"
    
        _ "github.com/go-sql-driver/mysql"
    )
    
    func main() {
        db1, err := sql.Open("mysql", `user1:password1@/dbname1`) // Сначала к одной БД подключение.
        if err != nil {
            log.Fatal(err)
        }
    
        db2, err := sql.Open("mysql", `user2:password2@/dbname2`) // Ниже к другой БД подключение.
        if err != nil {
            log.Fatal(err)
        }
    
        // настраиваете пулы как вам нужно
        db1.SetMaxIdleConns(10)
        db1.SetMaxOpenConns(100)
    
        db2.SetMaxIdleConns(10)
        db2.SetMaxOpenConns(100)
    }

    Ответ написан
  • Какой тип канала ставить?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Должно быть немного иначе, первое что нужно изменить - это объявление самой структуры, оно должно быть таким
    type Event struct {
            EventID int    `json:"eventId"`
            Type    string `json:"type"`
            ....
    }


    Судя по комментарию "//Делаем longpoll запрос на обновления" - логика должна быть немного иной, вы должны сделать один запрос и потом постоянно декодировать (по одному) пришедшие порции данных.
    Если я понял верно - тогда делать нужно вот так
    // канал создаёте вот так, т.к. вы хотите по одному событию передавать данные, а не целыми слайсами сразу
    EventsCh = make(chan Event, 100)
    go mailing(EventsCh)
    
    // делаете HTTP запрос
    response, responseErr := http.PostForm(...)
    
    // создаёте decoder
    decoder := json.NewDecoder(response.Body)
    
    for {
        event := Event{}
        decoderErr := decoder.Decode(&event) // если данных нет - тут выполнение программы блокируется
        if decodeErr != nil { // тут может быть ошибка io.EOF (просто данные закончились, сервер корректно закрыл соединение)
           break
        }
        EventsCh <- event  // отправляете данные в канал, из которого их читает горутина
    }


    Функция mailing должна быть такой
    func mailing(eventsCh chan Event){									//Получает события из канала
        for{
            event, ok := <- eventsChan
            if !ok {
                // канал закрыт
                break
            }
            log.Print("Получено ", event)
        }
    }
    Ответ написан
  • Как проверить длину массива в html шаблонах Golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вот так можно
    {{ $length := len .YourArray }}
    {{ if eq $length 0 }}
        array has zero length
    {{ end }}

    Просто вывести размер можно так
    {{ len .YourArray }}
    Ответ написан
    Комментировать
  • Как объединить массивы байтов в один массив?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно использовать bytes.Buffer (самая быстрая конкатенация) для Go, например так:
    messageReturn := bytes.NewBufferString("Your message is: ")
    messageReturn.Write(message)
    conn.WriteMessage(websocket.TextMessage, messageReturn.Bytes())
    Ответ написан
  • Как исправить ошибку при установке webview библиотеки в Golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вам нужно установить "devel" пакеты, что-то типа gtk+-devel и libwebkit2gtk-4.0-dev.
    Для разных дистрибутивов Линукса названия могут отличаться.

    Причина ошибки в том, что помимо установки самих пакетов нужны еще и пакеты, в которых, как минимум, есть заголовки (.h), чтобы была возможность скомпилировать ваш проект/webview.
    Ответ написан
    Комментировать
  • Как распарсить JSON на golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Пример
    // AddUserRequest структура с параметрами запроса.
    type AddUserRequest struct {
        Name string `json:"name"`
        Login string `json:"login"`
        Password string `json:"password"`
    }
    
    func AddUserHandlerFunc(w http.ResponseWriter, r *http.Request) {
            var req AddUserRequest
            err := json.NewDecoder(r.Body).Decode(&req);
            if err != nil {
            /// ....
            }
            // тут у вас будет заполненная структура req 
    }

    Запрос в формате JSON должен быть таким
    {
        "name": "Иван",
        "login": "Ivan",
        "password": "123"
    }


    Если структура JSON заранее не известна, можно использовать вот эту библиотеку
    https://github.com/valyala/fastjson
    Ответ написан
  • Как избежать большого количества аргументов в функциях?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    В идеале переделать.
    Если переделать нет возможности - структуры будут однозначно лучше.
    Еще есть вариант "функциональные опции" https://golang-blog.blogspot.com/2019/10/functiona...
    Ответ написан
    Комментировать
  • Почему у меня при тестировании в Golang FAIL?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вы три раза вызываете t.Errorf, как следствие получаете FAIL, чтобы тест был PASS - вызовова t.Errorf не должно быть. Т.е. вызывать t.Errorf нужно только в случае возникновения ошибки.

    Должно быть приблизительно так
    type twoValue struct {
        x int
        y int
        expected int
    }
    
    var tests = []twoValue{
        { 1,2, 1 },
        { 2,1, 2 },
        { 2,2, 0 },
    }
    
    func TestQuestion(t *testing.T) {
    
      for _, value := range tests {
        res := Question(value.x, value.y)
        if res != value.expected {
          t.Errorf("при сравнении %v с %v получили, %v, а должно быть %v", value.x, value.y, res, value.expected)
        }
      }
    }
    Ответ написан
  • Как ограничить отправку запросов из горутин?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    import "time"
    
    rate := time.Second / 10
    throttle := time.Tick(rate)
    for req := range requests {
      <-throttle  // rate limit our Service.Method RPCs
      go client.Call("Service.Method", req, ...)
    }

    Более реальный пример
    import "time"
    
    rate := time.Second / 10
    burstLimit := 100
    tick := time.NewTicker(rate)
    defer tick.Stop()
    throttle := make(chan time.Time, burstLimit)
    go func() {
      for t := range tick.C {
        select {
          case throttle <- t:
          default:
        }
      }  // does not exit after tick.Stop()
    }()
    for req := range requests {
      <-throttle  // rate limit our Service.Method RPCs
      go client.Call("Service.Method", req, ...)
    }


    Пример взял тут
    https://github.com/golang/go/wiki/RateLimiting
    Ответ написан
    Комментировать
  • Как сделать bulk insert в GORM?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно использовать библиотеку https://github.com/t-tiger/gorm-bulk-insert

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

    Примерно вот так:
    tx.Begin()
    for {
       DB.Create
    }
    tx.Commit()

    Советую присмотреться вот к этой библиотеке https://jmoiron.github.io/sqlx/
    Я её использую почти во всех проектах.
    Ответ написан
    Комментировать
  • Почему массив объектов, удовлетворяющих интерфейсу, не приводится к массиву этих интерфейсов?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вот так можете сделать
    items := make([]logged, 0)
    items = append(items, A{10})
    print(maxLogID(items))
    Ответ написан
  • Как организовать прерывание бесконечных циклов в тестах?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Закрыть канал в тесте, а функция читающая в цикле сообщения должна при этом корректно завершиться.
    Пример:
    data, ok := <-channel
    if !ok {
        return
    }
    Ответ написан
    Комментировать