Ответы пользователя по тегу Go
  • Как синхронизировать горутины?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Горутины синхронизировать/обмениваться данными можно при помощи каналов.
    У вас не очень удачный пример для обучения.
    Обычно есть горутина или несколько, которые получают данные и пишут их в канал и есть горутина или несколько, которые будут читать из канала.
    Читающие горутины будут получать данные из канала в той последовательности, в которой они туда попали.
    Ближе к реальности пример будет выглядеть вот так.
    Одна горутина пишет данные в канал, а две горутины по очереди извлекают данные.
    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    func main() {
        var wg sync.WaitGroup
    
        num := make(chan int, 1000) // 1000 - размер буффера канала
    
        wg.Add(1)
        go func() {
            for i := 0; i < 1000; i++ {
                num <- i
                fmt.Printf("write to channel: %d\n", i)
                // задержка нужна только на время теста
                time.Sleep(100 * time.Microsecond)
            }
            close(num)
            wg.Done()
        }()
    
        wg.Add(1)
        go func() {
            for {
                val, ok := <-num
                if !ok {
                    break
                }
                fmt.Printf("goroutine 1 got: %d\n", val)
            }
            wg.Done()
        }()
    
        wg.Add(1)
        go func() {
            for {
                val, ok := <-num
                if !ok {
                    break
                }
                fmt.Printf("goroutine 2 got: %d\n", val)
            }
            wg.Done()
        }()
    
        wg.Wait()
    }
    Ответ написан
    7 комментариев
  • Как обновлять поля в бд которые не являются пустой строкой go gorm?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Мне такого не встречалось, но, в принципе, при желании, можно сделать при помощи рефлексии.
    Но за рефлексию придётся заплатить производительностью :)
    Если нужен пример реализации - дайте знать, сделаю.
    Ответ написан
    4 комментария
  • Почему получаю только первый продукт из таблицы gorm go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Попробуйте вот так
    func GetProducts(c *gin.Context) {
      var products []models.Product
      database.DB.Find(&products)
      c.JSON(http.StatusOK, products)
    }
    Ответ написан
    3 комментария
  • Нормально ли я отрефакторил if-else?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Я бы посоветовал оставить начальный вариант, т.к. если сделаете banchmark'и - вы увидите, что он будет быстрее вашей реализации.
    Т.к. в начальной реализации нет доп. вызова функции, а в вашей есть getRulesResult.
    Чтобы вызвать функцию - нужно положить значения в стек, а по завершению извлечь.
    За счёт этого ваша реализация будет работать медленнее.
    Но это замечание справедливо только для функций, скорость выполнения которых критична, как сортировка например.

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

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

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

    Но вы однозначно на верном пути, принятые решения и подход мне нравятся, просто у вас не совсем удачная задача для рефакторинга выбрана, решение изначально хорошее )

    Обычно, код, который нужно рефакторить выглядит во много раз непонятнее, это может быть простыня на несколько экранов монитора :)), а когда в такой простыне еще и цикломатическая сложность огромная (вложенность if'ов и for'ов большая) - такой код вообще очень трудно понять :)
    Вот тут самое то для рефакторинга.
    Ответ написан
    3 комментария
  • Как мне получить body который был отправлен gin gonic golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вот так попробуйте
    jsonDataBytes, err := ioutil.ReadAll(c.Request.Body)
    Ответ написан
    6 комментариев
  • В чем может быть проблема с правами при попытке выполнения компиляции golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вам нужно разобраться правами.

    Если хотите чтобы собиралось от вашего пользователя, не под рутом - тогда
    нужно сделать чтобы вы были владельцем папки с проектом, это можно сделать вот так sudo chown -R ваш_логин /home/work/src и установить правильные права на папку /tmp chmod 777 /tmp

    Под рутом не находит go потому, что его бинарники не в PATH, нужно выполнить что-то типа такого export PATH=$PATH:/usr/lib/golang/bin
    Но нужно сначала найти где у вас лежит Go.
    Можно под обычным пользователем, где Go находит выполнить which go, ответ может быть таким /usr/lib/golang/bin/go - тогда нужно прописать как я написал.
    Если не получится - пишите, постараюсь помочь.
    Ответ написан
  • Как правильно конвертировать тип данных?

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

    Вот работающий пример
    package main
    
    import (
        "fmt"
        "log"
        "os"
        "strconv"
    )
    
    func main() {
    
        var what string
        var input string
    
        fmt.Print("Выберите действие (+, -)")
        fmt.Fscan(os.Stdin, &what)
        if what != `+` && what != `-` {
            log.Fatalf("действие указанно не корректно\n")
        }
    
        fmt.Print("Введите первое значение: ")
        fmt.Fscan(os.Stdin, &input)
        a, err := strconv.ParseFloat(input, 64)
        if err != nil {
            log.Fatalf("число указано не корректно: %v\n", err)
        }
    
        fmt.Print("Введите второе значение: ")
        fmt.Fscan(os.Stdin, &input)
        b, err := strconv.ParseFloat(input, 64)
        if err != nil {
            log.Fatalf("число указано не корректно: %v\n", err)
        }
    
        var c float64
        if what == "+" {
            c = a + b
        } else if what == "-" {
            c = a - b
        }
    
        fmt.Printf("Результат: %v\n", c)
    }

    Если это ваши первые шаги в программировании - лучше начать с Python, это ощутимо упростит старт.
    Ответ написан
    2 комментария
  • Gorilla Mux: не берет значение в vars := mux.Vars(r) Golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Через mux.Vars вы можете извлечь только то, что в URL, т.е. части URL, а параметры нужно извлекать через r.URL.Query().Get("param_name")

    Давайте разберём пример из реальной задачи.

    В URL содержится английское название категории (chairs), а также параметры фильтров width, height.
    https://domain.com/products/chairs?height=200&width=100


    Т.е. нужно отобразить все товары из категории "стулья" (chairs), но не выше 200 и не шире 100, соответственно нам нужно получить и chairs из URL и параметры.

    Роут будет таким в данном случае
    r.HandleFunc("/products/{category_handle}", h.ShowProducts).Methods("GET")


    А в обработчике будет уже вот так
    // из извлекаем из URL категорию
    vars := mux.Vars(r)
    categoryHandle := vars["categoryHandle"]
    
    // получаем параметры
    height, _ := strconv.Atoi(r.URL.Query().Get("height"))
    width, _ := strconv.Atoi(r.URL.Query().Get("width"))
    Ответ написан
    Комментировать
  • Как на 1 хостинге запустить несколько веб скриптов, на разных субдоменах?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Пишите Go lang сервисы, которые вам нужны.
    Каждый сервис слушает разный порт.
    Ставите веб сервер, например nginx и настраиваете виртуальные хосты, которые прокируют запросы к Go lang сервисам.
    Конфиг nginx'a будет выглядеть примерно так
    server {
        listen *:80;
        server_name yourdomain1.com; # домен, который вам нужно
        ...
        location / {
            proxy_pass http://127.0.0.1:9000; # IP адрес и порт, на котором слушает сервис Go lang.
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    
    server {
        listen *:80;
        server_name yourdomain2.com; # домен, который вам нужно
        ...
        location / {
            proxy_pass http://127.0.0.1:9001; # IP адрес и порт, на котором слушает сервис Go lang.
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    Ответ написан
    1 комментарий
  • Как сортировать в Go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если нужна именно функция - можно вот так
    func SortBytes(arr []byte) {
        sort.Slice(arr, func(i, j int) bool {
            return arr[i] < arr[j]
        })
    }
    // потом использовать вот так
    arr := []byte{4, 3, 2, 1, 6, 3, 77, 8,3}
    sortBytes(arr)
    fmt.Println(arr)

    Но я бы использовал тот вариант, который у вас )
    sort.Slice(arr, func(i, j int) bool {
      return arr[i] < arr[j]
    })
    fmt.Println(arr)
    Ответ написан
    5 комментариев
  • Как пробежаться по массиву и сделать Insert в бд?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если брать за основу ваш код - примерно так можно сделать
    package main
    
    import "fmt"
    
    type User struct {
      UserName string
      Category string
      Age      int
    }
    
    func main() {
    
      users := []User{
        User{UserName: "Bryan", Category: "Human", Age: 33},
        User{UserName: "Jane", Category: "Rocker", Age: 25},
        User{UserName: "Nancy", Category: "Mother", Age: 40},
        User{UserName: "Chris", Category: "Dude", Age: 19},
        User{UserName: "Martha", Category: "Cook", Age: 52},
      }
    
      // подключаетесь к базе
      db, _ := sql.Open(...)
      defer db.Close()
    
      for _, user := range users {
          fmt.Println(user)
          // делаете запрос в базу
          db.Exec("INSERT INTO users (name, category, age) VALUES ($1, $2, $3)", user.UserName, user.Category, user.Age)
      }
    }

    Если данных для вставки в базу будет много - такой вариант будет тормозить, в этом случае лучше делать вставку в транзакции
    tx, _ := db.Begin()  // начинаете транзакцию
    for _, user := range users {
        fmt.Println(user)
        // делаете запрос в базу (тут уже не db, а tx.Exec)
        tx.Exec("INSERT INTO users (name, category, age) VALUES ($1, $2, $3)", user.UserName, user.Category, user.Age)
    }
    tx.Commit() // завершаете транзакцию, данные сохраняются в базе
    Ответ написан
  • Как правильно работать с json в Go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вам нужно заменить Object []byte на Object json.RawMessage
    Ответ написан
    2 комментария
  • Работа с goroutines?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Есть несколько вариантов решения задачи
    передавать не указатель, а значение

    package main
    
    import (
      "fmt"
      "time"
    )
    
    var c = make(chan int, 3)
    var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    func main() {
      fmt.Println("Hello, playground")
      go save()
      go read()
      time.Sleep(3 * time.Second)
    }
    
    func save() {
      for _, val := range data {
        c <- val
      }
    }
    
    func read() {
      for {
        val := <-c
        fmt.Println("read:", val)
      }
    }

    скопировать значение и передать указатель на это значение

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    var c = make(chan *int)
    var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    func main() {
    	fmt.Println("Hello, playground")
    	go save()
    	go read()
    	time.Sleep(3 * time.Second)
    }
    
    func save() {
    	for _, val := range data {
    	        v := val
    		c <- &v
    	}
    }
    
    func read() {
    	for {
    		val := <-c
    		fmt.Println("read:", *val)
    	}
    }

    передать правильный указатель, на элемент данных

    package main
    
    import (
    	"fmt"
    	"time"
    
    
    var c = make(chan *int, 5)
    var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    func main() {
    	fmt.Println("Hello, playground")
    	go save()
    	go read()
    	time.Sleep(3 * time.Second)
    }
    
    func save() {
    	for i := range data {
    		c <- &data[i]
    	}
    }
    
    func read() {
    	for {
    		val := <-c
    		fmt.Println("read:", *val)
    	}
    }



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

    Вот пример, который покажет, что адрес всегда один и тот же
    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    var c = make(chan *int, 5)
    var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    func main() {
    	fmt.Println("Hello, playground")
    	go save()
    	go read()
    	time.Sleep(3 * time.Second)
    }
    
    func save() {
    	for _, val := range data {
    		c <- &val
    		fmt.Printf("write: %v\n", &val)
    	}
    }
    
    func read() {
    	for {
    		val := <-c
    		fmt.Println("read:", *val)
    	}
    }

    Вывод будет таким (адрес один и тот же)
    Hello, playground
    write: 0xc000094000
    write: 0xc000094000
    write: 0xc000094000
    write: 0xc000094000
    write: 0xc000094000
    write: 0xc000094000
    ...


    А в такой реализации (2й пример)
    func save() {
    	for _, val := range data {
    		v := val
    		c <- &v
    		fmt.Printf("write: %v\n", &v)
    	}
    }

    тут вывод будет таким (каждый раз новый участок памяти)
    Hello, playground
    write: 0xc000094000
    write: 0xc000094010
    write: 0xc000094018
    write: 0xc000094020
    write: 0xc000094028
    write: 0xc000094030

    В третьем варианте тоже будут разные указатели.
    Ответ написан
    5 комментариев
  • Как организовать код в интерфейсы golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Интерфейс нужно будет указывать как параметр функций, которые будут уметь работать с вашим интрейфейсом.
    Например.
    func Process(device test123.Command) {
        device.Open()
        device.Close()
    }
    
    dev1 := a123.A123struct{параметры...}
    Process(dev1)
    
    dev2 := b123.B123struct{параметры...}
    Process(dev2)
    
    // или с перечислением
    devices := []test123.Command{dev1, dev2}
    for _, device := range devices {
        Process(device)
    }


    Приведу пример более похожий на реальный.
    Допустим нам нужно сделать логгер, который умеет писать в файл (если указан в настройках), а если не указан - тогда в консоль.
    Пример намеренно упрощён, чтобы было проще уловить суть.
    package main
    
    import (
        "fmt"
        "os"
    )
    
    // Logger интерфейс логгера.
    type Logger interface {
        Error(msg string)
    }
    
    // StdoutLogger реализация интерфейса Logger для вывода сообщений в консоль.
    type StdoutLogger struct{}
    
    // NewStdoutLogger конструктор (возвращаем структуру, не интерфейс)
    func NewStdoutLogger() *StdoutLogger {
        return &StdoutLogger{}
    }
    
    // Error добавляет в лог сообщение с уровнем error
    func (l *StdoutLogger) Error(msg string) {
        fmt.Printf("ERROR: %s\n", msg)
    }
    
    // FileLogger реализация интерфейса Logger для вывода сообщений в файл.
    type FileLogger struct {
        FileName string
        Fh       *os.File
    }
    
    // NewFileLogger конструктор.
    func NewFileLogger(fileName string) *FileLogger {
        logger := &FileLogger{
            FileName: fileName,
        }
        fh, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0664)
        if err != nil {
            panic(fmt.Sprintf("FileLogger: can't open log file: %s: %s\n", fileName, err))
        }
        logger.Fh = fh
    
        return logger
    }
    
    // Error добавляет в лог сообщение с уровнем error.
    func (l *FileLogger) Error(msg string) {
        l.Fh.WriteString(fmt.Sprintf("ERROR: %s\n", msg))
    }
    
    // ProcessData какая то функция, которая использует логгер и которую не должна беспокоить реализация логгера.
    // Тут тип параметра должен быть интерфейс.
    func ProcessData(logger Logger) {
        logger.Error("Data process some error happened")
    }
    
    func main() {
        var logger Logger
    
        // если лог файл не указан - используем StdoutLogger, если указан - используем FileLogger
        logFile := os.Getenv("LOG_FILE")
        if logFile != `` {
            logger = NewFileLogger(logFile)
        } else {
            logger = NewStdoutLogger()
        }
    
        ProcessData(logger)
    }

    После запуска go run main.go вы увидите сообщение в консоли:
    ERROR: Data process some error happened
    а если запустите вот так LOG_FILE=test.log go run main.go
    или
    export LOG_FILE=test.log
    go run main.go

    То будет создан файл test.log и туда будет добавлено тоже сообщение, что вы видели в консоли.

    Если понятнее не стало - пишите, будут рад помочь.
    Ответ написан
    2 комментария
  • GoLang, подключение к базе - unexpected EOF в чем причина?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Как правило подобная ситуация говорит о проблемах с подключением к базе, драйверах(библиотеках).
    Первое, что имеет смысл сделать - это настроить соединения:
    db.SetConnMaxIdleTime(0)
    db.SetConnMaxLifetime(0)
    db.SetMaxIdleConns(10)
    db.SetMaxOpenConns(10)


    Если не помогло - нужно убедиться, что база действительно слушает указанный хост и порт в настройках.
    Для PostgreSQL
    psql -h 127.0.0.1 -p 5432 -U user_name  database_name

    Для MySQL
    mysql -h 127.0.0.1 -P 3307 -u user_name -p database_name

    Очень важно чтобы указано было именно 127.0.0.1 (для MySQL), иначе он всё равно будет подключаться по сокету несмотря на то, что указан localhost.

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

    Еще есть смысл смотреть логи базы, там может быть более подробно описана причина обрыва соединения.
    Ответ написан
    Комментировать
  • Почему у меня после Update, Select возвращает старые данные?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Проблема в этом участке, Query используется для извлечения данных, для изменения надо использовать Exec
    update_answer_id_start, err33 := db.Query(fmt.Sprintf("UPDATE `ucp` SET `closed` = '1' WHERE `id` = '%d'", res_trim_int))
    defer update_answer_id_start.Close()


    Например вот так
    update_answer_id_start, err33 := db.Exec("UPDATE `ucp` SET `closed` = '1' WHERE `id` = ?", res_trim_int)
    Ответ написан
    23 комментария
  • Проблема с склеиванием строк, где ошибка в коде?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Нужно внести правки в этот блок кода
    for res_select_list.Next(){
        err18 := res_select_list.Scan(&ucp.Id, &ucp.Text)
        if err18 != nil {
            panic(err18)
        }
        str.WriteString("LIST QUESTION:\r\nID- " + strconv.Itoa(ucp.Id) + " TEXT- " + ucp.Text)
    }


    Сделать вот так
    str.WriteString("LIST QUESTION:\r\n")
    for res_select_list.Next(){
        err18 := res_select_list.Scan(&ucp.Id, &ucp.Text)
        if err18 != nil {
            panic(err18)
        }
        str.WriteString("ID- " + strconv.Itoa(ucp.Id) + " TEXT- " + ucp.Text + "\r\n")
    }
    Ответ написан
    4 комментария
  • Применяется ли во встроенной функции copy отложенное копирование?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Отложенного копирования при копировании слайса нет.
    Вот исходник функции, которая копирует слайсы.
    https://golang.org/src/runtime/slice.go#L34
    В комментарии к функции написано:
    // makeslicecopy allocates a slice of "tolen" elements of type "et",
    // then copies "fromlen" elements of type "et" into that new allocation from "from".


    Исходный код функции на случай, если исходник не доступен

    // makeslicecopy allocates a slice of "tolen" elements of type "et",
    // then copies "fromlen" elements of type "et" into that new allocation from "from".
    func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer {
    	var tomem, copymem uintptr
    	if uintptr(tolen) > uintptr(fromlen) {
    		var overflow bool
    		tomem, overflow = math.MulUintptr(et.size, uintptr(tolen))
    		if overflow || tomem > maxAlloc || tolen < 0 {
    			panicmakeslicelen()
    		}
    		copymem = et.size * uintptr(fromlen)
    	} else {
    		// fromlen is a known good length providing and equal or greater than tolen,
    		// thereby making tolen a good slice length too as from and to slices have the
    		// same element width.
    		tomem = et.size * uintptr(tolen)
    		copymem = tomem
    	}
    
    	var to unsafe.Pointer
    	if et.ptrdata == 0 {
    		to = mallocgc(tomem, nil, false)
    		if copymem < tomem {
    			memclrNoHeapPointers(add(to, copymem), tomem-copymem)
    		}
    	} else {
    		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
    		to = mallocgc(tomem, et, true)
    		if copymem > 0 && writeBarrier.enabled {
    			// Only shade the pointers in old.array since we know the destination slice to
    			// only contains nil pointers because it has been cleared during alloc.
    			bulkBarrierPreWriteSrcOnly(uintptr(to), uintptr(from), copymem)
    		}
    	}
    
    	if raceenabled {
    		callerpc := getcallerpc()
    		pc := funcPC(makeslicecopy)
    		racereadrangepc(from, copymem, callerpc, pc)
    	}
    	if msanenabled {
    		msanread(from, copymem)
    	}
    
    	memmove(to, from, copymem)
    
    	return to
    }

    Ответ написан
    Комментировать
  • Как правильно декодировать JSON в UnmarshalJSON?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Есть не очень красивый вариант, но если нужно быстро решить задачу - можно использовать.
    Если у вас нагрузка небольшая - можно его использовать на постоянной основе.
    Если же нагрузка огромная - тогда надо иначе делать.
    Только везде, где символ `_` надо сделать обработку ошибок.
    package main
    
    import (
        "encoding/json"
        "fmt"
        "log"
    )
    
    type TemplateCategory struct {
        ID   int    `json:"id"`
        Name string `json:"name"`
    }
    
    type Template struct {
        Name         string            `json:"name"`
        CategoryInfo *TemplateCategory `json:"category_info"`
    }
    
    func (t *Template) UnmarshalJSON(b []byte) error {
        var result map[string]interface{}
        if err := json.Unmarshal(b, &result); err != nil {
            return err
        }
    
        if t == nil {
            t = &Template{}
        }
        t.Name, _ = result[`name`].(string)
    
        categoryInfo, isMap := result[`category_info`].(map[string]interface{})
        if isMap {
            t.CategoryInfo = &TemplateCategory{}
            t.CategoryInfo.ID, _ = categoryInfo[`id`].(int)
            t.CategoryInfo.Name, _ = categoryInfo[`name`].(string)
        }
    
        return nil
    }
    
    func main() {
        json1 := []byte(`{
            "name": "Мой шаблон",
            "category_info": {
                "id": 109,
                "name": "Тест"
            }
        }`)
    
        json2 := []byte(`{
            "name": "Мой шаблон",
            "category_info": []
        }`)
    
        var data1 Template
        err := json.Unmarshal(json1, &data1)
        if err != nil {
            log.Fatalf(`json1: %s`, err)
        }
    
        var data2 Template
        err = json.Unmarshal(json2, &data2)
        if err != nil {
            log.Fatalf(`json2: %s`, err)
        }
    
        fmt.Printf("data1: %+v\n", data1)
        fmt.Printf("data1.CategoryInfo: %+v\n\n", data1.CategoryInfo)
    
        fmt.Printf("\n\ndata2: %+v\n", data2)
        fmt.Printf("data2.CategoryInfo: %+v\n\n", data2.CategoryInfo)
    }

    Вывод:
    data1: {Name:Мой шаблон CategoryInfo:0xc00000c0c0}
    data1.CategoryInfo: &{ID:0 Name:Тест}

    data2: {Name:Мой шаблон CategoryInfo:}
    data2.CategoryInfo:
    Ответ написан
    7 комментариев
  • Проблема с отправкой сообщения, как решить?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Предполагаю вам нужно запрос в базу переместить в MessageNew.
    Т.е. алгоритм должен быть таким:
    - вы получили запрос /dump
    - сделали запрос в базу
    - в цикле извлекли данные и записали их в Buffer
    - одним сообщением ответили данными из Buffer

    Например так:
    lp.MessageNew(func(_ context.Context, obj events.MessageNewObject) {
        // тут приходит новый запрос
        if obj.Message.Text == "/dump" {
           var str strings.Builder
           // читаете данные из базы
           res, err := db.Query("SELECT `report_id`, `content_type` FROM `xf_report` WHERE `report_state` = 'open'")
           // по одном их обрабатываете и записываете в strings.Builder
           for res.Next(){
            err := res.Scan(&report.Report_id, &report.Content_type)
            str.WriteString(...)
          }
          // тут у вас все данные собраны в str
          // отправляете данные в ответ
          send, err2 := vk.MessagesSend(api.Params{
              "peer_id": 2000000001,
              "random_id": 0,
              "message": str.String(),
            })
        }

    Я убрал из кода обработку ошибок и т.д., чтобы вам было лучше видно суть.
    Ответ написан
    6 комментариев