Введение в golang. Часть 1.

Давайте начнем с небольшого знакомства с Go (или Golang). Go был разработан инженерами Google Робертом Гриземером, Робом Пайком и Кеном Томпсоном. Это статически типизированный, компилируемый язык. Первая версия была выпущена с открытым исходным кодом в марте 2012 года.

“Go – это язык программирования с открытым исходным кодом, который позволяет создавать простое, надежное и эффективное программное обеспечение”. – GoLang

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

Go, с другой стороны, верит в меньшее количество функций – с единственным правильным способом решения проблемы.

Это экономит время разработчиков и упрощает сопровождение большой кодовой базы. В Go нет таких “выразительных” функций, как карты и фильтры.

“Когда у вас есть функции, добавляющие выразительность, это обычно увеличивает расходы” – Роб Пайк
Введение в golang. Часть 1.
Недавно был опубликован новый логотип компании Go Lang: https://blog.golang.org/go-brand

Начало работы

Go состоит из пакетов. Пакет main сообщает компилятору Go, что программа компилируется как исполняемый файл, а не как общая библиотека. Он является точкой входа в приложение. Пакет main определяется как:

package main

Давайте перейдем к написанию простого примера hello world, создав файл main.go в рабочем пространстве Go.

Рабочее пространство

Рабочее пространство в Go определяется переменной окружения GOPATH.

Любой код, который вы пишете, должен быть написан внутри рабочей области. Go будет искать любые пакеты в каталоге GOPATH или в каталоге GOROOT, который задается по умолчанию при установке Go. GOROOT – это путь, по которому установлен go.

Установите в GOPATH нужный вам каталог. Пока что добавим его в папку ~/workspace.

# export env
export GOPATH=~/workspace
# go inside the workspace directory
cd ~/workspace

Создайте файл main.go со следующим кодом в папке рабочей области, которую мы только что создали.

Здравствуй, мир!

package main

import (
 "fmt"
)

func main(){
  fmt.Println("Hello World!")
}

В приведенном выше примере fmt – это встроенный пакет в Go, который реализует функции для форматирования ввода-вывода.

Мы импортируем пакет в Go с помощью ключевого слова import. func main – это основная точка входа, в которой выполняется код. Println – это функция внутри пакета fmt, которая печатает для нас “hello world”.

Давайте посмотрим, как запустить этот файл. Есть два способа запустить команду Go. Как мы знаем, Go – компилируемый язык, поэтому перед выполнением нам нужно его скомпилировать.

> go build main.go

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

> ./main 
# Hello World!

Есть и другой, более простой способ запустить программу. Команда go run помогает абстрагироваться от этапа компиляции. Вы можете просто выполнить следующую команду для выполнения программы.

go run main.go
# Hello World!

Примечание: Чтобы опробовать код, о котором говорится в этом блоге, вы можете воспользоваться сайтом https://play.golang.org.

Переменные

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

var a int

В этом случае значение будет равно 0. Чтобы объявить и инициализировать переменную с другим значением, используйте следующий синтаксис:

var a = 1

Здесь переменной автоматически присваивается значение int. Мы можем использовать сокращенное определение для объявления переменной как:

message := "hello world"

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

var b, c int = 2, 3

Типы данных

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

Число, строка и булево

Некоторые из поддерживаемых типов хранилища чисел: int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr…

Тип string хранит последовательность байтов. Он представляется и объявляется с помощью ключевого слова string.

Булево значение хранится с помощью ключевого слова bool.

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

var a bool = true
var b int = 1
var c string = 'hello world'
var d float32 = 1.222
var x complex128 = cmplx.Sqrt(-5 + 12i)

Массивы, фрагменты и карты

Массив – это последовательность элементов одного типа данных. Массивы имеют фиксированную длину, определенную при объявлении, поэтому их нельзя расширить больше, чем это необходимо. Массив объявляется как:

var a [5]int

Массивы также могут быть многомерными. Мы можем просто создать их в следующем формате:

var multiD [2][3]int

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

Слайсы хранят последовательность элементов и могут быть расширены в любой момент. Объявление срезов аналогично объявлению массивов – без определения емкости:

var b []int

При этом создается срез с нулевой емкостью и нулевой длиной. Ломтики также можно определить с вместимостью и длиной. Для этого можно использовать следующий синтаксис:

numbers := make([]int,5,10)

Здесь ломтик имеет начальную длину 5 и вместимость 10.

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

1*P0lNCO0sQwIYHLEX_mfSOQ
image src: https://blog.golang.org/go-slices-usage-and-internals

Емкость фрагмента можно увеличить с помощью функции append или copy. Функция append добавляет значение в конец массива и при необходимости увеличивает его емкость.

numbers = append(numbers, 1, 2, 3, 4)

Другой способ увеличить емкость среза – использовать функцию копирования. Просто создайте еще один срез с большей емкостью и скопируйте исходный срез в новый созданный срез:

// create a new slice
number2 := make([]int, 15)
// copy the original slice to new slice
copy(number2, number)

Мы можем создать под-фрагмент фрагмента. Это можно сделать с помощью следующей команды:

// initialize a slice with 4 len and values
number2 = []int{1,2,3,4}
fmt.Println(numbers) // -> [1 2 3 4]
// create sub slices
slice1 := number2[2:]
fmt.Println(slice1) // -> [3 4]
slice2 := number2[:3]
fmt.Println(slice2) // -> [1 2 3]
slice3 := number2[1:4]
fmt.Println(slice3) // -> [2 3 4]

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

var m map[string]int

Здесь m – новая переменная map, ключи которой являются строками, а значения – целыми числами. Мы можем легко добавлять ключи и значения в карту:

// adding key/value
m['clearity'] = 2
m['simplicity'] = 3
// printing the values
fmt.Println(m['clearity']) // -> 2
fmt.Println(m['simplicity']) // -> 3

Типизация

Один тип данных можно преобразовать в другой с помощью приведения типов. Рассмотрим простое преобразование типов:

a := 1.1
b := int(a)
fmt.Println(b)
//-> 1

Не все типы данных можно преобразовать в другой тип. Убедитесь, что тип данных совместим с преобразованием.

Условные утверждения

if else

Для условных операторов мы можем использовать операторы if-else, как показано в примере ниже. Убедитесь, что фигурные скобки находятся в той же строке, что и условие.

if num := 9; num < 0 {
 fmt.Println(num, "is negative")
} else if num < 10 {
 fmt.Println(num, "has 1 digit")
} else {
 fmt.Println(num, "has multiple digits")
}

switch case

Случаи switch помогают организовать несколько операторов условий. В следующем примере показан простой оператор switch case:

i := 2
switch i {
case 1:
 fmt.Println("one")
case 2:
 fmt.Println("two")
default:
 fmt.Println("none")
}

Looping

В Go есть единственное ключевое слово для цикла. Команда sngle for loop помогает достичь различных видов циклов:

i := 0
sum := 0
for i < 10 {
 sum += 1
  i++
}
fmt.Println(sum)

Приведенный выше пример похож на цикл while в C. Тот же оператор for может быть использован для обычного цикла for:

sum := 0
for i := 0; i < 10; i++ {
  sum += i
}
fmt.Println(sum)

Бесконечный цикл в Go:

for {
}

Указатели

Go предоставляет указатели. Указатели – это место для хранения адреса значения. Указатель определяется по *. Указатель определяется в зависимости от типа данных. Пример:

var ap *int

Здесь ap – указатель на целочисленный тип. Оператор & можно использовать для получения адреса переменной.

a := 12
ap = &a

К значению, на которое указывает указатель, можно обратиться с помощью оператора *:

fmt.Println(*ap)
// => 12

Указатели обычно используются при передаче структуры в качестве аргумента или при объявлении метода для определенного типа.

  1. При передаче значения оно фактически копируется, что требует больше памяти.
  2. При передаче указателя значение, измененное функцией, отражается обратно в вызывающий метод/функцию.

Пример:

func increment(i *int) {
  *i++
}
func main() {
  i := 10
  increment(&i)
  fmt.Println(i)
}
//=> 11

Примечание: Пока вы пробуете код примера в блоге, не забывайте включать его в пакет main и импортировать fmt или другие пакеты, когда это необходимо, как показано в первом примере main.go выше.

Функции

Функция main, определенная в пакете main, является точкой входа для выполнения программы go. Можно определить и использовать больше функций. Давайте рассмотрим простой пример:

func add(a int, b int) int {
  c := a + b
  return c
}
func main() {
  fmt.Println(add(2, 1))
}
//=> 3

Как видно из приведенного выше примера, функция Go определяется с помощью ключевого слова func, за которым следует имя функции. Аргументы, которые принимает функция, должны быть определены в соответствии с их типом данных, и, наконец, тип данных возврата.

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

func add(a int, b int) (c int) {
  c = a + b
  return
}
func main() {
  fmt.Println(add(2, 1))
}
//=> 3

Здесь c определена как возвращаемая переменная. Таким образом, определенная переменная c будет возвращена автоматически, без необходимости определять ее в конце оператора return.

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

func add(a int, b int) (int, string) {
  c := a + b
  return c, "successfully added"
}
func main() {
  sum, message := add(2, 1)
  fmt.Println(message)
  fmt.Println(sum)
}

Метод, структуры и интерфейсы

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

Структура

Структура – это типизированная коллекция различных полей. Структура используется для группировки данных. Например, если мы хотим сгруппировать данные типа Person, мы определяем атрибут person, который может включать имя, возраст, пол. Структура может быть определена с помощью следующего синтаксиса:

type person struct {
  name string
  age int
  gender string
}

Теперь, когда тип person struct определен, давайте создадим человека:

//way 1: specifying attribute and value
p = person{name: "Bob", age: 42, gender: "Male"}
//way 2: specifying only value
person{"Bob", 42, "Male"}

Мы можем легко получить доступ к этим данным с помощью точки(.).

p.name
//=> Bob
p.age
//=> 42
p.gender
//=> Male

Вы также можете получить доступ к атрибутам структуры непосредственно с помощью ее указателя:

pp = &person{name: "Bob", age: 42, gender: "Male"}
pp.name
//=> Bob

Методы

Методы – это особый тип функций с приемником. Приемником может быть как значение, так и указатель. Давайте создадим метод describe, который будет иметь приемник типа person, созданный нами в примере выше:

package main
import "fmt"

// struct defination
type person struct {
  name   string
  age    int
  gender string
}

// method defination
func (p *person) describe() {
  fmt.Printf("%v is %v years old.", p.name, p.age)
}
func (p *person) setAge(age int) {
  p.age = age
}

func (p person) setName(name string) {
  p.name = name
}

func main() {
  pp := &person{name: "Bob", age: 42, gender: "Male"}
  pp.describe()
  // => Bob is 42 years old
  pp.setAge(45)
  fmt.Println(pp.age)
  //=> 45
  pp.setName("Hari")
  fmt.Println(pp.name)
  //=> Bob
}

Как видно из приведенного выше примера, теперь метод можно вызвать с помощью оператора точки как pp.describe. Обратите внимание, что приемник – это указатель. С помощью указателя мы передаем ссылку на значение, поэтому, если мы внесем какие-либо изменения в метод, они будут отражены в приемнике pp. Также не создается новая копия объекта, что экономит память.

Обратите внимание, что в приведенном выше примере значение age изменяется, а значение name не изменяется, потому что метод setName имеет тип receiver, а setAge – тип pointer.

Интерфейсы

Интерфейсы Go – это набор методов. Интерфейсы помогают сгруппировать свойства типа. Рассмотрим пример интерфейса животного:

type animal interface {
  description() string
}

Здесь animal – это тип интерфейса. Теперь давайте создадим 2 разных типа животных, которые реализуют тип интерфейса animal:

package main

import (
  "fmt"
)

type animal interface {
  description() string
}

type cat struct {
  Type  string
  Sound string
}

type snake struct {
  Type      string
  Poisonous bool
}

func (s snake) description() string {
  return fmt.Sprintf("Poisonous: %v", s.Poisonous)
}

func (c cat) description() string {
  return fmt.Sprintf("Sound: %v", c.Sound)
}

func main() {
  var a animal
  a = snake{Poisonous: true}
  fmt.Println(a.description())
  a = cat{Sound: "Meow!!!"}
  fmt.Println(a.description())
}

//=> Poisonous: true
//=> Sound: Meow!!!

В функции main мы создаем переменную a типа animal. Мы присваиваем животному тип змея и кошка и с помощью Println выводим a.description. Поскольку мы реализовали метод describe в обоих типах (кошка и змея) по-разному, мы получим напечатанное описание животного.

Пакеты

Весь код на Go мы пишем в пакете. Главный пакет – это точка входа для выполнения программы. В Go существует множество встроенных пакетов. Самый известный из них, который мы использовали, – пакет fmt.

“Пакеты Go – это основной механизм программирования в большом, который предоставляет Go, и они позволяют разделить большой проект на более мелкие части”. — Robert Griesemer

Installing a package

go get <package-url-github>
// example
go get github.com/satori/go.uuid

Установленные нами пакеты сохраняются в папке GOPATH env, которая является нашей рабочей директорией. Вы можете увидеть пакеты, зайдя в папку pkg в нашей рабочей директории cd $GOPATH/pkg.

Создание пользовательского пакета

Начнем с создания папки custom_package:

> mkdir custom_package
> cd custom_package

Чтобы создать пользовательский пакет, нам нужно сначала создать папку с нужным нам именем пакета. Допустим, мы создаем пакет person. Для этого создадим папку с именем person внутри папки custom_package:

> mkdir person
> cd person

Теперь давайте создадим файл person.go в этой папке.

package person
func Description(name string) string {
  return "The person name is: " + name
}
func secretName(name string) string {
  return "Do not share"
}

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

> go install

Теперь давайте вернемся в папку custom_package и создадим файл main.go

package main
import(
  "custom_package/person"
  "fmt"
)
func main(){ 
  p := person.Description("Milap")
  fmt.Println(p)
}
// => The person name is: Milap

Теперь мы можем импортировать созданный нами пакет person и использовать функцию Description. Обратите внимание, что функция secretName, которую мы создали в пакете, будет недоступна. В Go имя метода, начинающееся без заглавной буквы, будет приватным.

Документация по пакетам

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

godoc person Description

Это создаст документацию для функции Description внутри нашего пакета person. Чтобы просмотреть документацию, запустите веб-сервер с помощью следующей команды:

godoc -http=":8080"

Теперь перейдите по URL http://localhost:8080/pkg/ и посмотрите документацию только что созданного нами пакета.

+1
0
+1
0
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *