Переменные и константы в Go

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

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

Что такое переменные

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

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

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

i := 1032049348

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

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

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

Позже мы увидим, как можно явно установить тип данных.

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

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

Например, мы можем использовать i вместо целого числа 1032049348. Давайте попробуем вывести его:

package main
import "fmt"
func main() {
i := 1032049348
fmt.Println(i)
}
1032049348

Также с помощью переменных можно быстро и легко решать арифметические выражения. Из переменной i: = 1032049348 можно вычесть целочисленное значение 813, к примеру:

fmt.Println(i - 813)
1032048535

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

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

x := 76 + 145

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

Давайте попробуем вывести x:

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

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

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

s := "Hello, World!"
f := 45.06
b := 5 > 9 // A Boolean value will return either true or 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)
}
[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)
}
var a int =  0
var b string = ""
var c float64 = 0
var d bool = false

Мы использовали оператор %T в выражении fmt.Printf. Он позволяет функции вывести data type для переменной.

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

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

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

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

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

Правильно Неправильно Почему нет
userName user-name Дефисы в именах переменных не допускаются
name4 4name Имя не может начинаться с числа
user $user Нельзя использовать символы
userName user name Имя не должно состоять из более чем одного слова

Кроме того, при именовании переменных имейте в виду, что они чувствительны к регистру. Это значит, что имена 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, но не использовать подчеркивания в именах из нескольких слов.

Принято Не принято Почему нет
userName user_name Подчеркивания обычно не используются
i index Имя i короче
serveHTTP serveHttp Акронимы должны записываться заглавными буквами

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

Переприсвоение переменных

Из самого слова «переменная» следует, что ее можно менять. Это означает, что с ранее назначенной переменной можно легко связать другое значение. Это делается путем переприсвоения. Функция переприсвоения полезна, потому что в коде программы нам может потребоваться принять пользовательские значения в уже инициализированные переменные. Также может возникнуть необходимость изменить значение или вернуть предыдущее значение.

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

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

package main
import "fmt"
func main() {
i := 76
fmt.Println(i)
i = 42
fmt.Println(i)
}
76
42

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

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

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

i := 72
i = "newString"

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

cannot use "newString" (type string) as type int in assignment

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

var s string
var s string
s redeclared in this block

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

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

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

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

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

j, k, l := "shark", 2.05, 15
fmt.Println(j)
fmt.Println(k)
fmt.Println(l)
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)
}
local
global

Здесь мы используем var g = «global», чтобы создать глобальную переменную вне функции. Затем мы определяем функцию printLoca (). Внутри функции назначается, а затем отображается локальная переменная 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)
}
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)
}
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)
}
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 = "Great"
fmt.Println(shark)
Great

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

cannot assign to shark

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

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)
}
8760
21900
21960

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

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

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

hours * year

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

minutes * year

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

minutes * leapYear

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

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

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

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

Читайте также:

Tags: