Go — это компилируемый строго типизированный язык программирования, разработанный в Google. Язык спроектирован для быстрой разработки высоконагруженных бэкендов. Если вы знакомы с императивными языками (например, C++, PHP, Java), то синтаксис Go будет понятен практически сразу:
import (
"encoding/json"
"errors"
"fmt"
)
type Message struct {
Sender string `json:"sender"` // ставим тег с описанием JSON поля
Text string `json:"text"`
}
// инициализация ошибки через конструктор стандартного пакета errors
var errEmptyMessage = errors.New("empty message")
// возвращаем ошибку в случае неожиданного поведения
func DecodeJSON(rawMsg string) (Message, error) {
// если нам передали пустую строку, возвращаем ошибку об этом
if len(rawMsg) == 0 {
return Message{}, errEmptyMessage
}
msg := Message{}
// декодируем строку в структуру
err := json.Unmarshal([]byte(rawMsg), &msg)
if err != nil {
return Message{}, fmt.Errorf("unmarshal: %w", err)
}
return msg, nil
}
В Go нет исключений. Вместо них используется встроенный интерфейс error
. Ошибки возвращаются явно последним аргументом из функции. Поэтому Go-код выглядит как череда вызовов функций и проверок на ошибки:
func main() {
msg, err := DecodeJSON("")
if errors.Is(err, errEmptyMessage) {
// { } empty message
fmt.Println(msg, err)
}
msg, err = DecodeJSON("hello")
if err != nil {
// { } unmarshal: invalid character 'h' looking for beginning of value
fmt.Println(msg, err)
}
msg, err = DecodeJSON(`{"sender":"hexlet","text":"Go,Go,Go"}`)
// {hexlet Go,Go,Go} <nil>
fmt.Println(msg, err)
}
Такой подход может показаться «неизящным» из-за постоянного повторения условного блока if err != nil
, однако он позволяет увидеть и контролировать все потенциальные ошибки в коде.
Самая сильная сторона Go — простое написание конкурентных программ. Для этого в языке используются легковесные потоки — горутины. Мы разберем эту тему подробно в соответствующем модуле, а пока можем оценить синтаксис программы, которая суммирует 10 значений из разных внешних источников:
import (
"fmt"
"sync"
)
func main() {
mu := sync.Mutex{}
wg := sync.WaitGroup{}
sum := 0
for i := 0; i < 10; i++ {
wg.Add(1)
// ставим перед любой функцией слово «go», и она выполняется конкурентно в горутине
go func() {
// делаем долгий вызов к стороннему API. Так как каждый вызов происходит в своей горутине, мы делаем 10 вызовов одновременно
n := externalHTTPNum()
mu.Lock()
sum += n
mu.Unlock()
wg.Done()
}()
}
// ждем, пока все 10 горутин вернут ответ
wg.Wait()
fmt.Println(sum) // 55
}
Не стоит расстраиваться, если сейчас что-то непонятно, или кажется сложным. После разбора концепций конкурентного программирования в Go и небольшой практики, вы будете с легкостью писать высокопроизводительный код.
Написать интересный код самостоятельно на текущем уровне будет затруднительно, поэтому скопируйте код ниже.
wg := sync.WaitGroup{}
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
fmt.Println("Go!")
wg.Done()
}()
}
wg.Wait()
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1package main
2
3import (
4 "fmt"
5 "log"
6 "os/exec"
7 "testing"
8)
9
10func TestGoGoGo(t *testing.T) {
11 cmd := exec.Command("go", "run", "solution.go")
12 out, err := cmd.CombinedOutput()
13 if err != nil {
14 log.Fatalf("exec command: %s\n", err)
15 }
16 fmt.Println(string(out))
17 // Output:
18 // Go!
19 // Go!
20 // Go!
21}
22
Решение учителя откроется через: