Въведение в Go

13.10.2015

Малко история

Малко история

C for the 21st century

"Трите свята"

Резултатът

Стандартната библиотека

archive tar zip bufio builtin bytes compress bzip2 flate gzip lzw zlib
container heap list ring crypto aes cipher des dsa ecdsa elliptic hmac
md5 rand rc4 rsa sha1 sha256 sha512 subtle tls x509 pkix database sql
driver debug dwarf elf gosym macho pe plan9obj encoding ascii85 asn1
base32 base64 binary csv gob hex json pem xml errors expvar flag fmt
go ast build constant doc format importer parser printer scanner token
types hash adler32 crc32 crc64 fnv html template image color palette
draw gif jpeg png index suffixarray io ioutil log syslog math big cmplx
rand mime multipart quotedprintable net http cgi cookiejar fcgi httptest
httputil pprof mail rpc jsonrpc smtp textproto url os exec signal user
path filepath reflect regexp syntax runtime cgo debug pprof race trace
sort strconv strings sync atomic syscall testing iotest quick text
scanner tabwriter template parse time unicode utf16 utf8 unsafe

Научените уроци

Малко код

package workq

type Queue []*Item

func (q *Queue) Push(item *Item) {
    *q = append(*q, item)
    go item.Translate()
}

func (q *Queue) Pop() *Item {
    if !q.IsEmpty() {
        old := *q
        item := old[0]
        <- item.Done
        *q = old[1:len(old)]
        return item
    }
    return nil
}

func (q *Queue) Len() int {
    return len(*q)
}

Защо Go?

Състезание по популярност

Go vs Java в Google Trends

Да сложим и C++ в картинката

Първата производна

Инсталация

* Linux, FreeBSD

Инсталирате си пакета go от вашия пакетен мениджър

* Mac OSX

* Windows

По време на курса ще използваме версия 1.5.1

Инсталация, епизод 2

1. Създавате директория ~/go
2. Слагате следните два реда в ~/.profile

export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

* За Windows-аджийте има уловки:

Hello, world!

За да сме сигурни, че сме направили всичко като хората, създаваме файл hello.go:

package main

import "fmt"

func main() {
    fmt.Printf("Hello, world!\n")
}

и изпълняваме

go run hello.go

Ако всичко изглежда наред, правим скрийншот и получаваме точка :)

Как да пишем код на Go?

Имаме инструментът go, който се грижи за достатъчно много неща.
Повечето настройки се правят с environment variables.
workspace, с три основни директории:

$ go

build       compile packages and dependencies
clean       remove object files
doc         show documentation for package or symbol
env         print Go environment information
fix         run go tool fix on packages
fmt         run gofmt on package sources
generate    generate Go files by processing source
get         download and install packages and dependencies
install     compile and install packages and dependencies
list        list packages
run         compile and run Go program
test        test packages
tool        run specified go tool
version     print Go version
vet         run go tool vet on packages

Environment variables

$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/user/go"
GORACE=""
GOROOT="/usr/lib/go"
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GO15VENDOREXPERIMENT=""
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"`

workspace

bin/
    streak                         # command executable
pkg/
    linux_amd64/
        code.google.com/p/goauth2/
            oauth.a                # package object
src/
    code.google.com/p/goauth2/
        .hg/                       # mercurial repository metadata
        oauth/
            oauth.go               # package source
            oauth_test.go          # test source
    github.com/nf/
        streak/
            .git/                  # git repository metadata
            oauth.go               # command source
            streak.go              # command source

Браво. И сега какво?

Връщаме се обратно към Hello, world! примера:

package main

import "fmt"

func main() {
    fmt.Printf("Hello, world!\n")
}

и се фокусираме върху

package main

Пакети

Една програма на Go е структура от пакети. Нямате шанс просто да хвърлите
едно парче код, в даден файл и то да тръгне.

Особености

Без значение от колко файла се състои:

One package to rule them all

Ако си мислите за "Lord of the rings", грешите. Говорим ви за main.

В една програма имаме точно един пакет main и при изпълнението ѝ се изпълнява функцията main в него.

Програмата приключва, когато приключи изпълнението на функцията main.

Тя не връща нищо.

Преизползване на пакети

Става с ключовата думичка import, последвана от името на пакета, в кавички

import "fmt"

Ако искаме да импортнем няколко пакета:

import "fmt"
import "os"

което go fmt би свел до

import (
    "fmt"
    "os"
)

import

package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Printf("Hello, world!\n")
}

Видимост

Какво точно можем да използваме от импортнат пакет? Тук става забавно.

Даден идентификатор (било то на променлива, константа, тип, функция или
метод) е видим извън пакета си, тогава и само тогава когато името му започва с главна буква.

Наричаме ги exported (на други места им викат public).

Останалите са недостъпни (демек private)

Основни типове

... но това не е всичко

Независещи от архитектурата числови типове

Имаме свободата да решим точно колко байта да е нашия тип:

... и разбира се имаме unsigned:

Зависещи от архитектурата

Пояснение

Типовете int и int32 са различни дори в архитектури на които int е 32 бита. Същото е и за uint.

Пример

var name string = "Чочко Чочков"
var age uint8 = 25

Горното можем (и трябва) да запишем като:

var (
    name string = "Чочко Чочков"
    age uint8 = 25
)

Ако са с един и същи тип, можем и така:

var name, age string = "Чочко Чочков", "двадесет и пет"

WTF! WTF! WTF! Защо типа е на грешното място!?

type-inference

Можем да правим и така:

name := "Чочко Чочков"
age := 25
package main

import "fmt"

name := "Киро"

func main() {
    fmt.Printf("%s, защо да не може?\n", name)
}

Стойности по подразбиране

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

var (
    digit int         // 0
    digit *int        // nil
    number float64    // 0.0
    isIt bool         // false
    name string       // ""
    root complex64    // (0+0i)
    pipe chan <type>  // nil
)

Кастване

Кастването работи, както очаквате:

number := 42                      // (int=42.0)
specificNumber := float64(number) // (float64=42.0)

С тази разлика, че нямате имплицитен каст:

number * specificNumber // (mismatched types int and float64)

Unused variable

Дефинирана стойност, която не се използва, води до грешка по време на компилация

package main

import "fmt"

func main() {
    name := "Кевин"
    fmt.Printf("Май забравихме някой?\n")
}

Константи

Аналогично с var:

const (
    name string = "Чочко Чочков"
    age uint8 = 25
)

Могат да бъдат само

iota

Нали помните enum?

const (
    Monday = iota
    Tuesday
    Wednesday
    Thursday
    Friday
    Partyday
    Sunday
)

Контролни структури

if

if age < 13 {
    fmt.Print("Още не си тийнейджър")
} else if age >= 13 && age < 20 {
    fmt.Print("В момента си тийнейджър")
} else {
    fmt.Print("Минали са ти тийнейджърските години")
}

for

Добрия стар for от C:

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

И точно както в C, някои от аргументите не са задължителни.
... някои да се чете като всички.

package main

import "fmt"

func main() {
    for {
        fmt.Println("Can't stop me!")
    }
}

Забележка: Отварящата { и тук е на същия ред с условието

while

Няма. for покрива всичките му приложения.

foreach

Няма. for покрива всичките му приложения.

switch

switch {
case age < 13:
    fmt.Print("Още не си тийнейджър")
case age >= 13 && age < 20:
    fmt.Print("В момента си тийнейджър")
default:
    fmt.Print("Минали са ти тийнейджърските години")
}

Няма нужда от излишния break

Ако искаме да изпълним два поредни case-а, използваме fallthrough.

break и continue

Все пак има break, който е приложим за for цикли.

continue работи, точно както очаквате и където очаквате.

goto

Да, има и такова.

Ако не знаете кога, не го ползвайте.

Към момента допускаме, че още не знаете кога => не го ползвате.

Особености, с които се сблъскахте (Q&A)

;

Q: Няма точка и запетая след всеки израз?

A: Всъщност има, но е имплицитна.

Сега си спомнете за правилото с отварящата {.

Индентация

Q: Интервали vs. Табове?
A: Един таб.

За четене

Q: Нещо за четене? Книги? Туториъли?
A:

В следващия епизод:

Въпроси?