Как использовать переменные и константы в Go

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

В этом руководстве будут рассмотрены основы переменных и лучшие практики их использования в программах на Go.

Понимание переменных

Технически переменная — это присвоение местоположения хранения значению, связанному с символическим именем или идентификатором. Мы используем имя переменной для ссылки на это сохраненное значение в компьютерной программе.

Можно представить переменную как ярлык с именем, который вы прикрепляете к значению.

Допустим, у нас есть целое число 1032049348, и мы хотим сохранить его в переменной, а не постоянно переписывать это длинное число. Для этого мы можем использовать легко запоминающееся имя, например, переменную i. Чтобы сохранить значение в переменной, мы используем следующий синтаксис:

i := 1032049348

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

На ярлыке написано имя переменной i, и он привязан к целочисленному значению 1032049348.

Фраза i := 1032049348 — это оператор объявления и присваивания, состоящий из нескольких частей:

  • имя переменной (i)
  • короткое объявление переменной с присваиванием (:=)
  • значение, которое присваивается имени переменной (1032049348)
  • тип данных, выводимый Go (int)

Мы увидим позже, как явно задать тип в следующем разделе.

Вместе эти части составляют оператор, который устанавливает переменную i равной значению целого числа 1032049348.

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

После того как мы установили i равным значению 1032049348, мы можем использовать i вместо целого числа, так что давайте выведем его:

package main

import "fmt"

func main() {
	i := 1032049348
	fmt.Println(i)
}
Output
1032049348

Мы также можем быстро и легко выполнять математические операции, используя переменные. С i := 1032049348 мы можем вычесть целочисленное значение 813 с помощью следующего синтаксиса:

fmt.Println(i - 813)
Output
1032048535

В этом примере Go выполняет вычисления за нас, вычитая 813 из переменной i и возвращая сумму 1032048535.

Говоря о математике, переменные могут быть установлены равными результату математического уравнения. Вы также можете сложить два числа и сохранить значение суммы в переменной x:

x := 76 + 145

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

Давайте выведем x:

package main

import "fmt"

func main() {
	x := 76 + 145
	fmt.Println(x)
}
Output
221

Go вернул значение 221, потому что переменная x была установлена равной сумме 76 и 145.

Переменные могут представлять любой тип данных, не только целые числа:

s := "Hello, World!"
f := 45.06
b := 5 > 9 // Логическое значение вернет либо true, либо false
array := [4]string{"item_1", "item_2", "item_3", "item_4"}
slice := []string{"one", "two", "three"}
m := map[string]string{"letter": "g", "number": "seven", "symbol": "&"}

Если вы выведете любую из этих переменных, Go вернет то, чему эта переменная эквивалентна. Давайте рассмотрим оператор присваивания для строкового типа данных slice:

package main

import "fmt"

func main() {
	slice := []string{"one", "two", "three"}
	fmt.Println(slice)
}
Output
[one two three]

Мы присвоили значение среза []string{"one", "two", "three"} переменной slice, а затем использовали функцию fmt.Println для вывода этого значения, вызвав slice.

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

Объявление переменных

В Go существует несколько способов объявить переменную, и в некоторых случаях более одного способа объявить одну и ту же переменную и значение.

Мы можем объявить переменную с именем i типа данных int без инициализации. Это означает, что мы объявим пространство для размещения значения, но не зададим ему начальное значение:

var i int

Это создает переменную, объявленную как i типа данных int.

Мы можем инициализировать значение с помощью оператора равенства (=), как в следующем примере:

var i int = 1

В Go оба этих вида объявления называются длинными объявлениями переменных.

Мы также можем использовать короткое объявление переменной:

i := 1

В этом случае у нас есть переменная с именем i и типом данных int. Когда мы не указываем тип данных, Go самостоятельно определит его.

С тремя способами объявления переменных сообщество Go приняло следующие идиомы:

  • Используйте длинную форму, var i int, только когда вы не инициализируете переменную.

  • Используйте короткую форму, i := 1, при объявлении и инициализации.

  • Если вы не хотите, чтобы Go определял тип данных, но все еще хотите использовать короткое объявление переменной, вы можете обернуть свое значение в желаемый тип с помощью следующего синтаксиса:

i := int64(1)

Использование длинной формы объявления переменной при инициализации значения не считается идиоматичным в Go:

var i int = 1

Хорошей практикой является следование тому, как обычно объявляют переменные в сообществе Go, чтобы другие могли легко читать ваши программы.

Нулевые значения

Все встроенные типы имеют нулевое значение. Любая выделенная переменная может быть использована, даже если ей никогда не было присвоено значение. Мы можем увидеть нулевые значения для следующих типов:

package main

import "fmt"

func main() {
	var a int
	var b string
	var c float64
	var d bool

	fmt.Printf("var a %T = %+v\n", a, a)
	fmt.Printf("var b %T = %q\n", b, b)
	fmt.Printf("var c %T = %+v\n", c, c)
	fmt.Printf("var d %T = %+v\n\n", d, d)
}
Output
var a int = 0 var b string = "" var c float64 = 0 var d bool = false

Мы использовали глагол %T в операторе fmt.Printf. Это говорит функции печатать тип данных для переменной.

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

Именование переменных: правила и стиль

Именование переменных довольно гибкое, но есть некоторые правила, о которых стоит помнить:

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

Следуя этим правилам, давайте рассмотрим как корректные, так и некорректные имена переменных:

Valid Invalid Why Invalid
userName user-name Hyphens are not permitted
name4 4name Cannot begin with a number
user $user Cannot use symbols
userName user name Cannot be more than one word

Кроме того, помните, что имена переменных чувствительны к регистру. Эти имена userName, USERNAME, UserName и uSERnAME являются полностью разными переменными. Лучше избегать использования похожих имен переменных в программе, чтобы обеспечить понимание их назначения как вам, так и вашим соавторам — как текущим, так и будущим.

Хотя переменные чувствительны к регистру, регистр первой буквы переменной имеет особое значение в Go. Если переменная начинается с заглавной буквы, то она доступна за пределами пакета, в котором была объявлена (или экспортирована). Если переменная начинается с строчной буквы, то она доступна только внутри пакета, в котором объявлена.

var Email string
var password string

Email начинается с заглавной буквы и может быть доступен другими пакетами. password начинается со строчной буквы и доступен только внутри пакета, в котором объявлен.

В Go принято использовать очень краткие (или короткие) имена переменных. Выбирая между userName и user для переменной, идиоматично выбрать user.

Область видимости также влияет на краткость имени переменной. Правило таково: чем меньше область видимости переменной, тем короче должно быть её имя:

names := []string{"Mary", "John", "Bob", "Anna"}
for i, n := range names {
	fmt.Printf("index: %d = %q\n", i, n)
}

Мы используем переменную names в более широкой области видимости, поэтому обычно ей дают более осмысленное имя, чтобы помочь запомнить, что она означает в программе. Однако переменные i и n мы используем сразу в следующей строке кода, а затем больше их не используем… По этой причине это не запутает читателя кода относительно того, где используются переменные, или что они означают.

Далее, давайте рассмотрим некоторые заметки о стиле переменных. Стиль заключается в использовании MixedCaps или mixedCaps вместо подчеркиваний для имен из нескольких слов.

Conventional Style Unconventional Style Why Unconventional
userName user_name Underscores are not conventional
i index prefer i over index as it is shorter
serveHTTP serveHttp acronyms should be capitalized

Самое важное в стиле — быть последовательным, и чтобы команда, с которой вы работаете, согласилась с этим стилем.

Переназначение переменных

Как следует из слова “переменная”, мы можем легко изменять переменные в Go. Это означает, что мы можем связать другое значение с ранее назначенной переменной через переназначение. Возможность переназначения полезна, потому что на протяжении всей программы нам может понадобиться принимать значения, сгенерированные пользователем, в уже инициализированные переменные. Нам также может понадобиться изменить назначение на что-то, что было определено ранее.

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

Давайте присвоим значение 76 переменной с именем i типа int, а затем присвоим ей новое значение 42:

package main

import "fmt"

func main() {
	i := 76
	fmt.Println(i)

	i = 42
	fmt.Println(i)
}
Output
76 42

Этот пример показывает, что мы можем сначала присвоить переменной i значение целого числа, а затем переназначить переменную i, присвоив ей на этот раз значение 42.

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

Поскольку Go является типизированным языком, мы не можем присваивать один тип другому. Например, мы не можем присвоить значение "Sammy" переменной типа int:

i := 72
i = "Sammy"

Пытаясь присвоить друг другу разные типы, мы получим ошибку компиляции:

Output
cannot use "Sammy" (type string) as type int in assignment

Go не позволит нам использовать имя переменной более одного раза:

var s string
var s string
Output
s redeclared in this block

Если мы попытаемся использовать короткое объявление переменной более одного раза для одного и того же имени переменной, мы также получим ошибку компиляции. Это может произойти случайно, поэтому понимание того, что означает сообщение об ошибке, полезно:

i := 5
i := 10
Output
no new variables on left side of :=

Так же, как и при объявлении переменной, учет её именования улучшит читаемость вашей программы для вас и других, когда вы вернетесь к ней в будущем.

Множественное присваивание

Go также позволяет нам присваивать несколько значений нескольким переменным в одной строке. Каждое из этих значений может быть разного типа:

j, k, l := "shark", 2.05, 15
fmt.Println(j)
fmt.Println(k)
fmt.Println(l)
Output
shark 2.05 15

В этом примере переменной j была присвоена строка "shark", переменной k было присвоено значение с плавающей точкой 2.05, а переменной l было присвоено целочисленное значение 15.

Такой подход к присваиванию нескольких переменных нескольким значениям в одной строке может сократить количество строк в вашем коде. Однако важно не жертвовать читаемостью ради меньшего количества строк кода.

Глобальные и локальные переменные

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

Глобальные переменные существуют вне функций. Локальные переменные существуют внутри функций.

Рассмотрим глобальные и локальные переменные на практике:

package main

import "fmt"


var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
}

func main() {
	printLocal()
	fmt.Println(g)
}
Output
local global

Здесь мы используем var g = "global" для создания глобальной переменной вне функции. Затем мы определяем функцию printLocal(). Внутри функции присваивается локальная переменная с именем l и затем выводится. Программа завершается вызовом printLocal() и последующим выводом глобальной переменной g.

Поскольку g является глобальной переменной, мы можем ссылаться на неё в printLocal(). Давайте модифицируем предыдущую программу, чтобы это сделать:

package main

import "fmt"


var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
	fmt.Println(g)
}

func main() {
	printLocal()
	fmt.Println(g)
}
Output
local global global

Мы начинаем с объявления глобальной переменной g, var g = "global". В функции main мы вызываем функцию printLocal, которая объявляет локальную переменную l и выводит её, fmt.Println(l). Затем printLocal выводит глобальную переменную g, fmt.Println(g). Несмотря на то, что g не была определена внутри printLocal, к ней всё равно можно было получить доступ, поскольку она была объявлена в глобальной области видимости. Наконец, функция main также выводит g.

Теперь попробуем вызвать локальную переменную вне функции:

package main

import "fmt"

var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
}

func main() {
	fmt.Println(l)
}

Output
undefined: l

Мы не можем использовать локальную переменную вне функции, в которой она была присвоена. Если вы попытаетесь это сделать, вы получите ошибку undefined при компиляции.

Рассмотрим другой пример, где мы используем одно и то же имя переменной для глобальной и локальной переменной:

package main

import "fmt"

var num1 = 5

func printNumbers() {
	num1 := 10
	num2 := 7  

	fmt.Println(num1)
	fmt.Println(num2)
}

func main() {
	printNumbers()
	fmt.Println(num1)
}
Output
10 7 5

В этой программе мы объявили переменную num1 дважды. Во-первых, мы объявили num1 в глобальной области видимости, var num1 = 5, и снова в локальной области видимости функции printNumbers, num1 := 10. Когда мы выводим num1 из программы main, мы видим, что выводится значение 5. Это происходит потому, что main видит только глобальное объявление переменной. Однако, когда мы выводим num1 из функции printNumbers, она видит локальное объявление и выводит значение 10. Несмотря на то, что printNumbers создает новую переменную с именем num1 и присваивает ей значение 10, это не влияет на глобальный экземпляр num1 со значением 5.

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

Константы

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

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

Для объявления константы мы можем использовать следующий синтаксис:

const shark = "Sammy"
fmt.Println(shark)
Output
Sammy

Если мы попытаемся изменить константу после её объявления, мы получим ошибку времени компиляции:

Output
cannot assign to shark

Константы могут быть untyped. Это может быть полезно при работе с числами, такими как данные целочисленного типа. Если константа untyped, она явно преобразуется, в то время как typed константы не преобразуются. Давайте посмотрим, как мы можем использовать константы:

package main

import "fmt"

const (
	year     = 365
	leapYear = int32(366)
)

func main() {
	hours := 24
	minutes := int32(60)
	fmt.Println(hours * year)    
	fmt.Println(minutes * year)   
	fmt.Println(minutes * leapYear)
}
Output
8760 21900 21960

Если вы объявляете константу с типом, она будет именно этим типом. Здесь, когда мы объявляем константу leapYear, мы определяем её как тип данных int32. Поэтому это typed константа, что означает, что она может работать только с данными типа int32. Константа year, которую мы объявляем без типа, поэтому она считается untyped. Из-за этого вы можете использовать её с любым целым типом данных.

Когда hours было определено, оно вывело тип int, потому что мы не указали его явно, hours := 24. Когда мы объявили minutes, мы явно указали его как int32, minutes := int32(60).

Теперь давайте рассмотрим каждый расчёт и почему он работает:

hours * year

В этом случае hours является int, а yearsнетипизированным. Когда программа компилируется, она явно преобразует years в int, что позволяет успешно выполнить операцию умножения.

minutes * year

В этом случае minutes является int32, а yearнетипизированным. Когда программа компилируется, она явно преобразует years в int32, что позволяет успешно выполнить операцию умножения.

minutes * leapYear

В этом случае minutes является int32, а leapYearтипизированной константой типа int32. На этот раз компилятору нечего делать, так как обе переменные уже имеют один и тот же тип.

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

fmt.Println(hours * leapYear)
Output
invalid operation: hours * leapYear (mismatched types int and int32)

В этом случае hours был выведен как int, а leapYear был явно объявлен как int32. Поскольку Go — типизированный язык, int и int32 несовместимы для математических операций. Чтобы их умножить, вам нужно преобразовать один в int32 или int.

Заключение

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

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-variables-and-constants-in-go