Переменные являются важной частью программирования. Это символы, которые обозначают в программе присвоенное им значение.
В этом мануале вы найдете основы работы переменных и рекомендации по их использованию в создаваемых вами программах 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 несовместимы для выполнения математических операций. Чтобы умножить их, вам нужно преобразовать один тип в другой.
Читайте также: