One place for hosting & domains

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


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

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

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

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

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

      Переменные в Go

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

      i := 1032049348
      

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

      Пример переменной в Go

      На ярлыке указано название переменной 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 // 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)
      }
      

      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 = %+vn", a, a)
          fmt.Printf("var b %T = %qn", b, b)
          fmt.Printf("var c %T = %+vn", c, c)
          fmt.Printf("var d %T = %+vnn", d, d)
      }
      

      Output

      var a int = 0 var b string = "" var c float64 = 0 var d bool = false

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

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

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

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

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

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

      ДопустимоНедопустимоПричина недопустимости
      userNameuser-nameДефисы не разрешены
      name44nameНе могут начинаться с цифры
      user$userНе могут содержать символы
      userNameuser 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 = %qn", i, n)
      }
      

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

      Добавим несколько замечаний о стиле переменных. В состоящих из нескольких слов именах используется формат MixedCaps или mixedCaps, а не символы подчеркивания.

      Стандартный стильНестандартный стильПочему нестандартный
      userNameuser_nameСимвол подчеркивания нестандартный
      iindexi предпочтительнее index, поскольку он короче
      serveHttpserveHttpАббревиатуры следует писать с заглавной буквы

      Для стиля очень важна согласованность, а также использование единого стиля всеми членами команды.

      Изменение назначения переменных

      Как подразумевает слово «переменная», мы можем легко изменять переменные 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

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

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

      При определении 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 link


      Leave a Comment