Съдържание на курса

Урок 2 — Променливи, функции и типове

От Dorian Chávez · основател на Hábil и архитект на интеграции ·

Продължителност: 90 минути или две сесии по 45.

Като приключиш, ще можеш:

  • Да обясниш какво прави компилаторът на Go с твоя текстов файл.
  • Да декларираш променливи и да знаеш кога да използваш := и кога =.
  • Да назовеш четирите основни типа и да кажеш защо Go не ти позволява да ги смесваш.
  • Да пишеш функции, които получават и връщат стойности.
  • Да разбереш защо една функция на Go може да върне две неща едновременно.
  • Да четеш съобщенията за грешки на компилатора и да знаеш какво иска от теб.
  • Да напишеш, компилираш и изпълниш пълна програма сам.

Защо е важно

revisor — програмата, която ще изграждаш през целия курс — има нужда още от първия си ред от три неща: данни (името на една услуга, нейния порт, колко време ѝ е отнело да отговори), логика, която да реши нещо с тези данни (бърза ли е, или бавна?), и начин да предупреди, когато нещо се е объркало (услугата не е отговорила?). Точно това, в този ред, преподава този урок: променливи, за да пазиш данни, функции за логиката и шаблона с две върнати стойности за грешките.

Не е случайно, че Go е компилиран и със статични типове: и двете решения съществуват, за да може компютърът да хване грешките ти преди програмата да се изпълни, вместо да ги открие потребител в продукция. Интерпретираният език те оставя да напишеш puerto = "muchos" и гърми едва когато този ред се изпълни — което може да е десет минути по-късно или никога, ако този път в кода почти не се използва. Go отказва да създаде изпълнимия файл. Тази разлика е дълбоката причина за почти всичко, което ще видиш в този урок: защо компилаторът е строг, защо съществува „нулевата стойност“ и защо функция, която може да се провали, трябва да го каже в сигнатурата си, а не като изненада.

В края на урока ще напишеш първата истинска стъпка на revisor (проектно упражнение): функция, която получава името на една услуга, нейния порт и времето ѝ за отговор и връща класифициран ред от отчета. Все още без structs, без конкурентност и без HTTP — те идват по-късно — но вече е код, който прилича на крайната програма.


Понятията

2.1 Какво прави компилаторът

Пишеш текстов файл. Сам по себе си този файл не прави нищо: той е текст. За да може компютърът да го изпълни, нещо трябва да го преведе в инструкциите, които процесорът разбира.

Има два начина да се направи този превод:

Как работи Езици
Интерпретиран Една програма чете текста ти ред по ред и прави това, което пише, всеки път, когато го изпълниш Python, JavaScript, PHP
Компилиран Една програма превежда целия ти текст само веднъж и записва резултата в изпълним файл Go, C, C++, Rust

Go е компилиран и това има три последици, които ще забележиш още от днес:

  1. Има стъпка преди изпълнението: компилирането. Ако си сбъркал името на някоя променлива, разбираш там — преди програмата да се изпълни. При интерпретиран език щеше да разбереш, когато изпълнението стигне до този ред, което може да е десет минути по-късно или никога.
  2. Резултатът е файл, който работи сам. Не е нужно другият компютър да има инсталиран Go.
  3. Бърз е при изпълнение, защото преводът вече е направен.

2.2 Първата ти програма, ред по ред

Ще напишем, компилираме и изпълним пълна програма. Всички програми в този курс са пълни и изпълними: никакви фрагменти, които не могат да се пуснат.

Подготви папката:

mkdir -p ~/w/curso-go/cap01 && cd ~/w/curso-go/cap01
go mod init cap01

Фиг. 2.1 | Програма, която отпечатва съобщение.

 1  // fig02_01.go
 2  // Imprime un mensaje en la pantalla.
 3  package main
 4
 5  import "fmt"
 6
 7  func main() {
 8      fmt.Println("Bienvenido a Go!")
 9  }

Компилирай я и я изпълни:

$ go run fig02_01.go
Bienvenido a Go!

А сега всеки ред, защото всеки има причина:

Редове 1-2. Коментари. Започват с // и компилаторът ги пренебрегва напълно. Те са за онзи, който чете кода — включително ти след три седмици.

Ред 3: package main. Всеки файл на Go принадлежи на пакет, който е група от код. Пакетът main е специален: той е единственият, който създава изпълнима програма. Ако напишеш package utilidades, ще имаш библиотека, която други програми могат да използват, но която не може да се изпълни сама.

Ред 5: import "fmt". Казва на компилатора, че ще използваш код от пакета fmt (от format), който идва с Go и съдържа функциите за отпечатване и форматиране на текст. Go не зарежда нищо по подразбиране: всичко, което използваш, го поискваш.

Ред 7: func main() {. Декларира функцията main, която е мястото, откъдето започва изпълнението на програмата. Когато изпълниш програма на Go, системата търси точно тази функция. Ако я наречеш principal или inicio, програмата няма да тръгне.

Ред 8: fmt.Println("Bienvenido a Go!"). Извиква функцията Println, която се намира в пакета fmt. Точката се чете като „от“: „функцията Println от fmt“. Println отпечатва това, което ѝ дадеш, и минава на следващия ред (print line).

Ред 9: }. Затваря тялото на функцията. В Go фигурните скоби ограждат блокове, също както в C, C++, Java или JavaScript.

2.2.1 Разликата между go run и go build

Фиг. 2.2 | Двата начина да изпълниш програмата си.

$ go run fig02_01.go        # compila en memoria, ejecuta, y no deja archivo
Bienvenido a Go!

$ go build fig02_01.go      # compila y GUARDA el ejecutable
$ ls -la
-rwxr-xr-x  1 usuario usuario 1841624  fig02_01      ← el programa
-rw-r--r--  1 usuario usuario     104  fig02_01.go   ← tu texto

$ ./fig02_01                # y se ejecuta directo
Bienvenido a Go!

Обърни внимание на размерите: текстът ти е 104 байта; компилираната програма — 1.8 мегабайта. Разликата е, че изпълнимият файл носи в себе си всичко, от което има нужда, за да работи.

2.3 Променливи

Променливата е място в паметта с име, в което пазиш данни, които програмата ти ще използва.

Фиг. 2.3 | Деклариране на променливи и отпечатването им.

 1  // fig02_03.go
 2  // Declara variables de distintos tipos y las imprime.
 3  package main
 4
 5  import "fmt"
 6
 7  func main() {
 8      nombre := "catalogo"                 // texto
 9      puerto := 443                      // número entero
10      tiempo := 0.142                    // número con decimales
11      seguro := true                     // verdadero o falso
12
13      fmt.Println("servicio:", nombre)
14      fmt.Println("puerto:", puerto)
15      fmt.Println("tiempo de respuesta:", tiempo)
16      fmt.Println("usa HTTPS:", seguro)
17  }
$ go run fig02_03.go
servicio: catalogo
puerto: 443
tiempo de respuesta: 0.142
usa HTTPS: true

Операторът := се чете „декларирай нова променлива и запиши в нея това“. Go гледа стойността отдясно и сам извежда типа: "catalogo" е в кавички, значи е текст; 443 няма кавички и няма точка, значи е цяло число.

2.3.1 := срещу =

Щом променливата вече съществува, за да ѝ смениш стойността, използваш = без двоеточието:

Фиг. 2.4 | Създаване и промяна на променлива.

 1  // fig02_04.go
 2  package main
 3
 4  import "fmt"
 5
 6  func main() {
 7      puerto := 443          // la CREA con valor 443
 8      fmt.Println(puerto)
 9
10      puerto = 8080          // le CAMBIA el valor
11      fmt.Println(puerto)
12  }
$ go run fig02_04.go
443
8080

2.3.2 Go отказва да компилира, ако не използваш някоя променлива

Фиг. 2.5 | Програма, която не се компилира, нарочно.

 1  // fig02_05.go — este programa NO compila
 2  package main
 3
 4  import "fmt"
 5
 6  func main() {
 7      nombre := "catalogo"
 8      edad := 30              // se declara y nunca se usa
 9      fmt.Println(nombre)
10  }
$ go run fig02_05.go
# command-line-arguments
./fig02_05.go:8:5: declared and not used: edad

Go не го позволява. Това не е предупреждение, което можеш да пренебрегнеш: това е грешка, която спира компилацията.

Защо толкова строго? Защото неизползваната променлива почти винаги означава едно от две неща: сбъркал си името на друг ред или е останал боклук от код, който си изтрил наполовина. И двете са проблеми. Go предпочита да те подразни днес, отколкото да остави мъртъв код да се трупа завинаги.

2.4 Основните типове

Типът е отговорът на въпроса „какъв вид данни е това?“. Четирите, които ще използваш през цялото време:

Тип Какво пази Примери Нулева стойност
string текст "hola", "https://catalogo.example.com" "" (празен)
int цели числа 42, -7, 0 0
float64 числа с десетична част 3.14, 0.142 0
bool истина или лъжа true, false false

2.4.1 Go не смесва типове

Фиг. 2.6 | Още една програма, която не се компилира, нарочно.

 1  // fig02_06.go — este programa NO compila
 2  package main
 3
 4  import "fmt"
 5
 6  func main() {
 7      puerto := 443
 8      puerto = "muchos"       // intenta guardar texto en un entero
 9      fmt.Println(puerto)
10  }
$ go run fig02_06.go
# command-line-arguments
./fig02_06.go:8:14: cannot use "muchos" (untyped string constant) as int value in assignment

В Python това би работило без оплакване. В Go не се компилира и това е огромно предимство:

2.4.2 Нулевата стойност: в Go никога няма боклук

Фиг. 2.7 | Променливи, декларирани без стойност.

 1  // fig02_07.go
 2  // Muestra el valor cero de cada tipo basico.
 3  package main
 4
 5  import "fmt"
 6
 7  func main() {
 8      var texto string
 9      var entero int
10      var decimal float64
11      var logico bool
12
13      fmt.Printf("string:  %q\n", texto)
14      fmt.Printf("int:     %d\n", entero)
15      fmt.Printf("float64: %v\n", decimal)
16      fmt.Printf("bool:    %t\n", logico)
17  }
$ go run fig02_07.go
string:  ""
int:     0
float64: 0
bool:    false

Формата var nombre tipo декларира променлива, без да ѝ дава стойност. В други езици това би оставило „боклук“ — каквото е имало в тази памет — или специално състояние „недефинирано“. В Go тя винаги получава валидна стойност, наречена нулева стойност.

И тук се появява Printf, която е различна от Println: получава шаблон с празни места и ги попълва едно по едно. Всяко празно място започва с %:

За какво
%d цяло число (decimal)
%s текст (string)
%q текст с кавички (quoted) — полезно, за да видиш дали нещо е празно
%t bool (true/false)
%v каквото и да е, в естествения му формат (value)
\n нов ред (Printf не минава сама на нов ред, Println — да)

2.5 Функции

Функцията е именуван блок от код, на който можеш да подадеш данни и който може да ти върне резултат. Вече използва две: fmt.Println и fmt.Printf.

Фиг. 2.8 | Дефиниране и извикване на функции.

 1  // fig02_08.go
 2  // Define funciones que reciben y devuelven valores.
 3  package main
 4
 5  import "fmt"
 6
 7  // sumar recibe dos enteros y devuelve su suma.
 8  func sumar(a int, b int) int {
 9      return a + b
10  }
11
12  // etiqueta arma un texto descriptivo del servicio.
13  func etiqueta(nombre string, puerto int) string {
14      return fmt.Sprintf("%s:%d", nombre, puerto)
15  }
16
17  // esSeguro dice si el puerto corresponde a HTTPS.
18  func esSeguro(puerto int) bool {
19      return puerto == 443
20  }
21
22  func main() {
23      fmt.Println("2 + 3 =", sumar(2, 3))
24      fmt.Println(etiqueta("catalogo", 443))
25      fmt.Println("catalogo es seguro:", esSeguro(443))
26      fmt.Println("local es seguro:", esSeguro(8080))
27  }
$ go run fig02_08.go
2 + 3 = 5
catalogo:443
catalogo es seguro: true
local es seguro: false

Анатомия на ред 8, където е всичко:

func  sumar  (a int, b int)  int  {
 │      │          │          │
 │      │          │          └── lo que DEVUELVE
 │      │          └── lo que RECIBE (los parámetros), con su tipo
 │      └── el nombre
 └── palabra clave: «voy a definir una función»

Ред 14: fmt.Sprintf. Като Printf е, но вместо да отпечата, връща сглобения текст. S е от string. Използва се изключително много.

Ред 19: puerto == 443. Двойното равно сравнява и дава true или false. Единичното равно = присвоява. Объркването им е класика (виж „Какво се прави погрешно“ по-долу).

2.6 Връщане на две стойности: подписът на Go

Това е най-характерното за Go и ще го пишеш хиляди пъти в кариерата си. Внимавай.

Помисли за функция, която дели две числа. Какво прави, ако делителят е нула? Не може да върне число, защото резултатът не съществува.

Други езици използват изключения: функцията „хвърля“ грешка и някой по-нагоре я „хваща“ с try / catch. Проблемът е, че не е очевидно кой я хваща, нито къде, и е изключително лесно никой да не го направи и програмата да умре.

Go прави нещо друго: връща две стойности. Резултата и грешка.

Фиг. 2.9 | Функция, която връща резултат и евентуална грешка.

 1  // fig02_09.go
 2  // Devuelve dos valores: el resultado y un posible error.
 3  package main
 4
 5  import (
 6      "errors"
 7      "fmt"
 8  )
 9
10  // dividir devuelve el cociente y un error si el divisor es cero.
11  func dividir(a int, b int) (int, error) {
12      if b == 0 {
13          return 0, errors.New("division entre cero")
14      }
15      return a / b, nil
16  }
17
18  func main() {
19      // caso que funciona
20      resultado, err := dividir(10, 2)
21      if err != nil {
22          fmt.Println("error:", err)
23      } else {
24          fmt.Println("10 / 2 =", resultado)
25      }
26
27      // caso que falla
28      resultado, err = dividir(10, 0)
29      if err != nil {
30          fmt.Println("error:", err)
31      } else {
32          fmt.Println("10 / 0 =", resultado)
33      }
34  }
$ go run fig02_09.go
10 / 2 = 5
error: division entre cero

Ред 11: (int, error). Скобите с два типа означават „тази функция връща две неща“: цяло число и грешка.

Ред 13: return 0, errors.New(...). Когато има проблем, връща някаква произволна стойност (тук 0, която няма да се използва) и грешка, която описва какво се е случило.

Ред 15: return a / b, nil. Когато всичко е наред, връща резултата и nil.

🔑 nil означава „нищо“, „празно“, „няма“. Тогава въпросът if err != nil се чете: „грешката различна ли е от нищо?“, или на нормален език: „имаше ли грешка?“

Редове 20-21: шаблонът, който определя Go.

resultado, err := dividir(10, 2)
if err != nil {
    // algo salió mal: aquí se atiende
}
// si llegaste aquí, todo bien

Този шаблон if err != nil е най-често писаният ред в цялата история на Go. Появява се навсякъде и има хора, които го критикуват като повтарящ се.

2.7 Да съберем всичко заедно

Фиг. 2.10 | Пълна програма, която използва всичко от урока.

 1  // fig02_10.go
 2  // Clasifica servicios por su tiempo de respuesta.
 3  package main
 4
 5  import "fmt"
 6
 7  // clasificar traduce milisegundos a una etiqueta legible.
 8  func clasificar(ms int) string {
 9      if ms < 0 {
10          return "sin medir"
11      } else if ms < 500 {
12          return "rapido"
13      } else if ms < 3000 {
14          return "lento"
15      }
16      return "muy lento"
17  }
18
19  // reporte arma una linea del informe.
20  func reporte(nombre string, puerto int, ms int) string {
21      return fmt.Sprintf("%-10s :%-5d %6dms  %s",
22          nombre, puerto, ms, clasificar(ms))
23  }
24
25  func main() {
26      fmt.Println("SERVICIO   PUERTO  TIEMPO   ESTADO")
27      fmt.Println("-------------------------------------")
28      fmt.Println(reporte("catalogo", 443, 142))
29      fmt.Println(reporte("pagos", 443, 87))
30      fmt.Println(reporte("inventario", 443, 2310))
31      fmt.Println(reporte("reportes", 3001, 4500))
32      fmt.Println(reporte("viejo", 8080, -1))
33  }
$ go run fig02_10.go
SERVICIO   PUERTO  TIEMPO   ESTADO
-------------------------------------
catalogo   :443      142ms  rapido
pagos      :443       87ms  rapido
inventario :443     2310ms  lento
reportes   :3001    4500ms  muy lento
viejo      :8080      -1ms  sin medir

Тази програма вече е пряк предшественик на revisor: clasificar и reporte са, в миниатюра, това, което в урок 3 ще се превърне в метод на struct Servicio, а в урок 6 ще се изпълнява за няколко услуги едновременно.

Ред 21: %-10s и %-5d. Числото казва колко знака ширина да се запазят, а знакът минус означава подравнено вляво. Така колоните остават прави. Без това таблицата би излязла крива.


Грешката, която ще видиш

Почти всички съобщения за грешка на Go имат една и съща форма:

<archivo>:<línea>:<columna>: <qué esperaba el compilador y qué encontró>

Да се научиш да четеш този ред е половината работа. Два примера, които вече видя в този урок:

./fig02_05.go:8:5: declared and not used: edad

Чете се: „във файла fig02_05.go, ред 8, колона 5, си декларирал edad и никога не си я използвал“. Поправката е лесна: или използваш променливата, или я изтриваш, или — ако наистина ти трябва декларирана, но още не я използваш — я заменяш с празния идентификатор _.

./fig02_06.go:8:14: cannot use "muchos" (untyped string constant) as int value in assignment

Чете се: „на ред 8, колона 12, опита да използваш текста "muchos" там, където се очакваше int, и не ти позволявам“. Поправката обаче не е лесна за отгатване, ако никога не си я виждал: не съществува автоматично преобразуване между string и int при такова присвояване. Ще трябва да преобразуваш изрично (със strconv, който ще видим по-нататък) или, по-вероятно, да осъзнаеш, че си объркал типа по грешка.

И трета, най-честата през първата седмица, която се появява, ако използваш отново := върху променлива, която вече съществува:

./fig02_04.go:10:9: no new variables on left side of :=

Чете се: „от лявата страна на := няма нито една нова променлива“ — всички вече са съществували, така че Go не знае какво да декларира. Поправката: използвай = вместо :=, когато вече си декларирал променливата по-рано.

Общото правило: компилаторът на Go почти никога не казва „нещо се обърка“ абстрактно. Казва файла, реда, колоната и фраза, която — макар да звучи странно първия път — описва точно проблема. Да я прочетеш цялата, без да се стряскаш, решава повечето случаи, без да се налага да търсиш нещо в интернет.

Какво се прави погрешно

  • Писане на println с малка буква вместо Println. Go различава главни от малки букви: Println и println са различни имена (println е вътрешна функция на компилатора, предназначена за отстраняване на грешки в самия Go, а не в твоята програма). Грешката е undefined: fmt.println.
  • Използване на import "fmt" без да го използваш, или използване на fmt.Println без import. Първото дава "fmt" imported and not used; второто — undefined: fmt. И двете са един и същи вид грешка: казал си на компилатора нещо, което не съвпада с онова, което си направил.
  • Объркване на = (присвояване) с == (сравнение) вътре в if. В C това би се компилирало и би направило нещо неочаквано (би присвоило стойността и би продължило нататък). В Go това е грешка при компилация — cannot use puerto = 443 (...) as value —, което е добра новина: спира те, преди програмата да направи нещо, което не си поискал.
  • Забравяне на \n в Printf. Програмата се компилира и се изпълнява, но всичко излиза слепено на един ред. Println минава на нов ред автоматично; Printf — не. Това е тиха грешка, а не такава, която компилаторът да ти посочи.
  • Пренебрегване на грешката с _, ето така: resultado, _ := dividir(10, 0). И това се компилира, и програмата продължава напред с resultado, равно на 0, сякаш всичко е наред. Това е най-бързият начин да създадеш грешка, която после е невъзможно да откриеш, защото няма никакво съобщение, нито срив: програмата просто продължава с невярна стойност. Ако някога изхвърлиш грешка с _, нека това бъде съзнателно и коментирано решение, а никога рефлекс, за да спре компилаторът да се оплаква.

Упражнения

Въпроси за преговор

Отговори, без да гледаш отговорите. Те са в края на този раздел.

2.1 Каква е разликата между компилиран и интерпретиран език и кой от двата е Go?

2.2 Защо функцията, от която започва програмата, трябва да се казва точно main?

2.3 Каква е разликата между := и =?

2.4 Каква грешка дава Go, ако декларираш променлива и не я използваш? Защо мислиш, че го прави?

2.5 Какво е нулевата стойност и каква е тя за string, int и bool?

2.6 Какво отпечатва тази програма?

package main

import "fmt"

func main() {
    var n int
    var s string
    fmt.Printf("[%d] [%q]\n", n, s)
}

2.7 Каква е разликата между Println, Printf и Sprintf?

2.8 Какво означава nil и какво пита if err != nil?

2.9 Тази програма има три грешки, които пречат да се компилира. Намери ги, без да я пускаш.

package main

func main() {
    nombre := "catalogo"
    puerto := 443
    puerto := 8080
    fmt.Println(nombre)
}

Отговори

2.1 Интерпретираният език се превежда ред по ред при всяко изпълнение; компилираният се превежда изцяло само веднъж и създава изпълним файл. Go е компилиран.

2.2 Защото това е конвенция на езика: при изпълнение на програма Go търси функцията main от пакета main, за да започне. С друго име не намира откъде да тръгне.

2.3 := декларира нова променлива и ѝ присвоява стойност; = само присвоява на вече съществуваща.

2.4 declared and not used. Прави го, защото неизползваната променлива почти винаги показва грешка — сбъркано име или остатъци от изтрит код — и Go предпочита да те спре, отколкото да остави мъртъв код.

2.5 Това е стойността, която автоматично получава променлива, декларирана без стойност. string → "", int → 0, bool → false. Гарантира, че никога няма боклук, нито „недефинирано“.

2.6 [0] [""] — нулевата стойност на int и на string, а %q показва кавичките.

2.7 Println отпечатва и минава на нов ред. Printf отпечатва с шаблон с % и не минава сама на нов ред. Sprintf използва същия шаблон, но връща текста, вместо да го отпечата.

2.8 nil означава „нищо“ / „празно“. if err != nil пита „грешката не е ли празна?“, тоест „имаше ли грешка?“.

2.9 Трите:

  1. Липсва import "fmt", а се използва fmt.Println.
  2. puerto := 8080 използва := върху променлива, която вече съществува → трябва да е =.
  3. puerto се декларира и никога не се използва (отпечатва се само nombre) → declared and not used.

Упражнения с код

🔴 Тези седем упражнения все още нямат еталонно решение — то ще бъде добавено в по-късна версия. Не е измислен код за решение, за да не се публикува пример, без първо да е изпълнен.

2.10 Напиши програма, която декларира името ти, възрастта ти и дали учиш, или работиш, и ги отпечатва на три реда с Printf, като използваш правилния глагол за всеки тип.

2.11 Напиши func celsiusAFahrenheit(c float64) float64 и я изпробвай с 0, 37 и 100. Формулата е f = c*9/5 + 32. Внимание: ако напишеш c*9/5 с цели числа, резултатът се отрязва. Защо тук това не се случва?

2.12 Напиши func esPar(n int) bool. Подсказка: операторът % дава остатъка от деление, така че n % 2 == 0 е вярно за четните числа.

2.13 Напиши func raizCuadrada(n float64) (float64, error), която връща грешка, ако n е отрицателно. Използвай math.Sqrt (трябва ти import "math"). Изпробвай я с 16 и с -4, като обработиш грешката и в двата случая.

2.14 Вземи Фиг. 2.10 и ѝ добави колона, която казва SI или NO според това дали портът е 443. Трябва ти нова функция и да промениш шаблона на Sprintf.

2.15 (Намери грешката) Всеки от тези фрагменти има проблем. Кажи го, без да компилираш, а после компилирай, за да потвърдиш:

// (a)
resultado := dividir(10, 2)

// (b)
func sumar(a, b) int { return a + b }

// (c)
var x int = "5"

// (d)
fmt.Printf("el total es %d")

2.16 (Проект на курса) Това е първата стъпка от програмата, която ще изграждаш през целия курс. Напиши програма, която:

  1. Има функция revisar(nombre string, puerto int, ms int) string, която връща ред от отчета.
  2. Има функция clasificar(ms int) string.
  3. Отпечатва заглавен ред и пет услуги.
  4. Накрая отпечатва колко от петте са били "rapido". Ще ти трябват променлива брояч и if.

Запази я: в урок 3 ще я преустроиш със structs.

Как разбирам, че съм успял

  • Компилира и пусна, сам и без копиране/поставяне, фигури 2.1 до 2.10 от този урок, и получи точно показания изход.
  • Можеш да обясниш, без да гледаш текста, защо Go е компилиран език и каква практическа последица има това за хващането на грешки преди продукция.
  • Знаеш наизуст кога да използваш := и кога = и каква грешка дава компилаторът, ако сбъркаш.
  • Можеш да назовеш четирите основни типа (string, int, float64, bool) и нулевата стойност на всеки от тях, без да поглеждаш таблицата.
  • Написа и пусна своя собствена версия на упражнение 2.16 (първата стъпка на revisor): компилира се, отпечатва заглавния ред, петте реда и броя на бързите услуги.
  • Можеш да обясниш с две изречения защо if err != nil се появява навсякъде в кода на Go и какъв проблем избягва в сравнение с изключенията на други езици.

Отвори bitacora.md и запиши две неща: грешката на компилатора, която ти е било най-трудно да разбереш, и нещо, което те е изненадало. След три седмици ще ти изглежда очевидно и няма да помниш защо ти е било трудно — а точно това си струва да е записано.


Резюме

  • Go е компилиран език: превежда целия ти код веднъж и създава самостоятелен изпълним файл.
  • Всеки файл принадлежи на пакет; пакетът main е единственият, който създава изпълним файл.
  • Изпълнението започва от функцията main.
  • import внася пакети; Go не зарежда нищо по подразбиране.
  • := декларира и присвоява; = само присвоява.
  • Go не се компилира, ако декларираш променлива и не я използваш, нито ако смесваш типове.
  • Четирите основни типа са string, int, float64 и bool.
  • Нулевата стойност гарантира, че всяка променлива се ражда с валидна стойност: "", 0, 0, false.
  • Println отпечатва с нов ред; Printf използва шаблон с %; Sprintf връща текста, вместо да го отпечата.
  • Функция на Go може да върне няколко стойности, а стандартният шаблон е да се върне (resultado, error).
  • nil означава „нищо“. if err != nil е идиоматичният начин да провериш дали е имало грешка, и най-често писаният ред в Go.
  • go run компилира и изпълнява, без да оставя файл; go build оставя изпълнимия файл.

За допълнително четене

  1. A Tour of Go — официална, интерактивна. Мини частта „Basics“ дотам, докъдето си стигнал в този урок.
  2. Go by Example — Variables и Go by Example — Multiple Return Values — същият шаблон (resultado, error) с минимални програми, които се изпълняват.
  3. Effective Go — още не е нужно да го четеш целия, но прегледай го, ако нещо от този урок ти се е сторило произволно правило: там е обяснението защо.

Ако нещо не ти е станало ясно: прочети цялото съобщение за грешка (Go обикновено казва реда, колоната и какво е очаквал), потърси понятието в Go by Example и запиши въпроса в дневника, дори да не го решиш. Записан въпрос може да се реши по-късно; забравен — не.

Термини от този урок

компилатор програма, която превежда изходен код в машинни инструкции
грешка при компилация грешка, която пречи да се създаде изпълнимият файл; открива се преди изпълнението
функция именуван блок от код, който получава параметри и може да връща стойности
празен идентификатор (_) символ, който изхвърля стойност нарочно
int, float64, string, bool четирите основни типа
компилиран / интерпретиран език превежда всичко веднъж / ред по ред при всяко изпълнение
main (функция) входна точка на програмата
main (пакет) единственият пакет, който създава изпълним файл
nil липса на стойност
пакет група от свързан код
параметър данни, които една функция получава
Printf / Println / Sprintf отпечатване с шаблон / отпечатване с нов ред / връщане на текст
тип вид данни, които една променлива може да пази
нулева стойност валидна стойност, която получава всяка променлива, декларирана без стойност
променлива именувано място в паметта, което пази данни
форматиращ глагол (verb) (%d, %s, %q, %t, %v) празно място в шаблон на Printf
Да обсъдим вашия проект

Предпочитате имейл? Пишете ни на hola@habil.mx