Базовые конструкции языка

Лекция 2

Максим Иванов

Имена

25 ключевых слов.

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var
2

Predeclared identifiers

Константы

true false iota nil

Типы

any comparable int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error

Функции

min max make len cap new append clear copy close delete
complex real imag
panic recover

Идентификаторы можно переопределять по обычным правилам

// just example, don't do this
// Backward compatibility
var true = false
3

Declaration

4

var

Общая форма

var name type = expression

Примеры

var i, j, k int // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string
var f, err = os.Open(name) // os.Open returns a file and an error
5

short variable declaration

i, j := 0, 1

Существующим переменным присваиваются новые значения

in, err := os.Open(infile)
// ...
out, err := os.Create(outfile)

Но должна объявляться хотя бы одна новая переменная

f, err := os.Open(infile)
// ...
f, err := os.Create(outfile) // compile error: no new variables
6

pointers

x := 1
p := &x         // p, of type *int, points to x
fmt.Println(*p) // "1"
*p = 2
fmt.Println(x)
7

flag

package main

import (
    "flag"
    "fmt"
    "strings"
)

var (
    n   = flag.Bool("n", false, "omit trailing newline")
    sep = flag.String("s", " ", "separator")
)

func main() {
    flag.Parse()
    fmt.Print(strings.Join(flag.Args(), *sep))
    if !*n {
        fmt.Println()
    }
}
8

new

new(T) создаёт новую переменную с типом *T.

func newInt() *int {
    return new(int)
}

func newInt() *int {
    var dummy int
    return &dummy
}
9

C++

...
if (full_data)
{
    auto value = getPointsData();
    full_points_data = &value;
}
auto table = getPointsByDocument(doc_id, full_points_data);
...

Что не так?

10

escape analysis Go

var p = f()

func f() *int {
    v := 1
    return &v
}
11

variable lifetime

Память освобождается, после того как переменная становится недостижимой.

Компилятор может переместить переменную со стека на кучу.

var global *int
func f() {
    var x int
    x = 1
    global = &x
}

И с кучи на стек.

func g() {
    y := new(int)
    *y = 1
}
12

type declaration

type name underlying-type
package tempconv

type (
    Celsius    float64
    Fahrenheit float64
)

const (
    AbsoluteZeroC Celsius = -273.15
    FreezingC     Celsius = 0
    BoilingC      Celsius = 100
)

func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
type name = another-type
13

packages

14

packages and types

package example

const (
    C0 = 9.8
    c1 = 15
)

var (
    V0 string
    v1 string
)

func F0() {}
func f1() {}

type (
    T0 int
    t1 string
)
15

packages and types

package tempconv

import "fmt"

type (
    Celsius    float64
    Fahrenheit float64
)

const (
    AbsoluteZeroC Celsius = -273.15
)

func (c Celsius) String() string    { return fmt.Sprintf("%g°C", c) }
func (f Fahrenheit) String() string { return fmt.Sprintf("%g°F", f) }
16

packages and types

package tempconv

func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }

Implicit cast только для нетипизированных констант

fmt.Printf("Brrrr! %v\n", tempconv.AbsoluteZeroC)

fmt.Println(tempconv.CToF(tempconv.BoilingC))
17

package initialization

var (
    a = b + c // a initialized third, to 3
    b = f()   // b initialized second, to 2, by calling f
    c = 1     // c initialized first, to 1
)

func f() int { return c + 1 }
18

package initialization

package popcount

// pc[i] is the population count of i.
var pc [256]byte

func init() {
    for i := range pc {
        pc[i] = pc[i/2] + byte(i&1)
    }
}

// PopCount returns the population count (number of set bits) of x.
func PopCount(x uint64) int {
    return int(pc[byte(x>>(0*8))] +
        pc[byte(x>>(1*8))] +
        pc[byte(x>>(2*8))] +
        pc[byte(x>>(3*8))] +
        pc[byte(x>>(4*8))] +
        pc[byte(x>>(5*8))] +
        pc[byte(x>>(6*8))] +
        pc[byte(x>>(7*8))])
}
19

scope

package main

import "fmt"

// nolint
func main() {
    x := "hello"
    for _, x := range x {
        x := x + 'A' - 'a'
        fmt.Printf("%c", x) // "HELLO" (one letter per iteration)
    }
    fmt.Println()
    fmt.Println(x) // hello
}
20

scope

package main

import "fmt"

func f() int      { return 0 }
func g(x int) int { return x }

func example() {
    if x := f(); x == 0 {
        fmt.Println(x)
    } else if y := g(x); x == y {
        fmt.Println(x, y)
    } else {
        fmt.Println(x, y)
    }
}
21

scope if

if f, err := os.Open(fname); err != nil { // compile error: unused: f
    return err
}
f.ReadByte() // compile error: undefined f
f.Close()    // compile error: undefined f
f, err := os.Open(fname)
if err != nil {
    return err
}
f.ReadByte()
f.Close()
22

scope

var cwd string

func init() {
    cwd, err := os.Getwd() // NOTE: wrong!
    if err != nil {
        log.Fatalf("os.Getwd failed: %v", err)
    }
    log.Printf("Working directory = %s", cwd)
}
23

strings

string - неизменяемая последовательность байт.

s[i] - обращается к i-тому байту (не символу).

var s = "hello"

var doc = `Go is a tool for managing Go source code.

Usage:
go command [arguments]
...
`
24

unicode

Символы кодируются числами.

type rune = int32

Кодировка utf8

0xxxxxxx                              runes 0−127
110xxxxx 10xxxxxx                     128−2047
1110xxxx 10xxxxxx 10xxxxxx            2048−65535
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   65536−0x10ffff

Разница между рунами и байтами

import "unicode/utf8"

func countRunes() {
    s := "Hello, 世界"
    fmt.Println(len(s)) // "13" - Почему?
    fmt.Println(utf8.RuneCountInString(s)) // "9"
}
25

utf8

for i := 0; i < len(s); {
    r, size := utf8.DecodeRuneInString(s[i:])
    fmt.Printf("%d\t%c\n", i, r)
    i += size
}

Декодирование utf8 встроено в язык

for i, r := range "Hello, 世界" {
    fmt.Printf("%d\t%q\t%d\n", i, r, r)
}
// 0    'H'    72
// 1    'e'    101
// 2    'l'    108
// 3    'l'    108
// 4    'o'    111
// 5    ','    44
// 6    ' '    32
// 7    '世'    19990
// 10    '界'    30028
26

utf8

runes := []rune("Hello, 世界")
s := string(runes)
fmt.Println(s)  // Hello, 世界

Некорректный байт превращается unicode replacement character '\uFFFD'.

27

utf8

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := string([]byte{0xff, 0xfe, 'A'}) // два невалидных байта + 'A'
    t := string([]rune(s))

    fmt.Printf("s: len=%d, valid=%v\n", len(s), utf8.ValidString(s)) // 3, false
    fmt.Printf("t: len=%d, valid=%v\n", len(t), utf8.ValidString(t)) // 7, true
    fmt.Printf("%q -> %q\n", s, t)                                   // "\xff\xfeA" -> "��A"
}
28

stdlib

29

[]byte

s := "abc"
b := []byte(s)
s2 := string(b)
fmt.Println(s == s2) // true
30

bytes.Buffer

func intsToBytes(values []int) []byte {
    var buf bytes.Buffer
    buf.WriteByte('[')
    for i, v := range values {
        if i > 0 {
            buf.WriteString(", ")
        }
        fmt.Fprintf(&buf, "%d", v)
    }
    buf.WriteByte(']')
    return buf.Bytes()
}
31

constants

const (
    a = 1
    b
    c = 2
    d
)

fmt.Println(a, b, c, d) // "1 1 2 2"

type Weekday int
const (
    Sunday Weekday = iota
    Monday
    Tuesday
)

type Flags uint
const (
    FlagUp Flags = 1 << iota // is up
    FlagBroadcast            // supports broadcast access capability
    FlagLoopback             // is a loopback interface
    FlagPointToPoint         // belongs to a point-to-point link
    FlagMulticast            // supports multicast access capability
)
32

untyped constants

const (
    _ = 1 << (10 * iota)
    KiB
    MiB
    GiB
    TiB // (exceeds 1 << 32)
    PiB
    EiB
    ZiB // (exceeds 1 << 64)
    YiB
)
fmt.Println(YiB/ZiB) // "1024"
33

arrays

var a [3]int
var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2} // [1 2 0]

d := [...]int{1, 2, 3}

x := [...]int{3: -1, 5: -100}
fmt.Printf("%T %v", x, x) // [6]int [0 0 0 -1 0 -100]
func zero(ptr *[32]byte) {
    *ptr = [32]byte{}
}
34

slices

s := make([]int, 10)
s = s[:0]
s = s[:10]
s = s[:5:5]
s = s[:10] // panic
a := [10]int{}
s := a[:]
35

slices

// reverse reverses a slice of ints in place.
func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}
36

slices

var a, b []string
// a == nil && b == nil
fmt.Println(a == b) // invalid operation: a == b (slice can only be compared to nil)

var c []int
d := []int{}

// c == nil && d != nil
// len(c) == 0 && len(d) == 0
37

slices

make([]T, len) // len == cap
make([]T, len, cap)

func append(s []T, elem ...T) []T

var s []int
s = append(s, 1)
s = append(s, 2, 3)

var a, b []int
a = append(a, b...)

sCopy := append([]int(nil), s...) // use slices.Clone instead
38

stack

stack = append(stack, v) // push v

top := stack[len(stack)-1] // top of stack

stack = stack[:len(stack)-1] // pop
39

queue

queue = append(queue, v) // push v

front := queue[0] // front of queue

queue = queue[1:] // pop
40

copy

func remove(slice []int, i int) []int {
    copy(slice[i:], slice[i+1:])
    return slice[:len(slice)-1]
}
41

maps

ages := make(map[string]int)

ages := map[string]int{
    "alice": 31,
    "charlie": 34,
}

ages := make(map[string]int)
ages["alice"] = 31
ages["charlie"] = 34
42

maps

ages["alice"] = 32
fmt.Println(ages["alice"]) // "32"
fmt.Println(ages["bob"]) // "0"

delete(ages, "alice")

ages["bob"] += 1
ages["bob"]++
_ = &ages["bob"] // compile error: cannot take address of map element

clear(ages)
43

maps

for name, age := range ages {  // random
    fmt.Printf("%s\t%d\n", name, age)
}

var ages map[string]int
fmt.Println(ages == nil)    // "true"
fmt.Println(len(ages) == 0) // "true"
fmt.Println(ages["alice"])  // "0"
ages["alice"] = 21          // panic
44

maps

age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }

if age, ok := ages["bob"]; !ok { /* ... */ }
45

set

var s0 map[string]bool
var s1 map[string]struct{}
46

struct

type Employee struct {
    ID      int
    Name    string
    Address string
    Salary  int
}

var dilbert Employee

dilbert.Salary -= 5000 // demoted, for writing too few lines of code
47

struct

type tree struct {
    value       int
    left, right *tree
}
48

struct

type tree struct {
    value       int
    left, right *tree
}
49

struct

type Point struct{ X, Y int }
p := Point{1, 2}
p := Point{X: 1, Y: 2}
50

comparing structs

type Point struct{ X, Y int }
p := Point{1, 2}
q := Point{2, 1}
fmt.Println(p.X == q.X && p.Y == q.Y) // "false"
fmt.Println(p == q)                   // "false"

Можно использовать структуры как ключи

type address struct {
    hostname string
    port     int
}

hits := make(map[address]int)
hits[address{"golang.org", 443}]++
51

zero size type

p := new(int)
q := new(int)
fmt.Println(p == q) // "false"

p := new(struct{})
q := new(struct{})
fmt.Println(p == q) // "true" or "false", depending on implementation

a := [1_000_000_000]struct{}{}
fmt.Println(unsafe.Sizeof(a)) // 0
52

struct embedding (тизер)

type Point struct {
    X, Y int
}

type Circle struct {
    Point
    Radius int
}

c := Circle{
    Point:  Point{X: 10, Y: 10},
    Radius: 1,
}

c.X = 0
53

json

type Movie struct {
    Title  string
    Year   int  `json:"year"`
    Color  bool `json:"color,omitempty"`
    Actors []string
}
data, err := json.Marshal(movies)
if err != nil {
    log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)

data, err := json.MarshalIndent(movies, "", "    ")
if err != nil {
    log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
54

unmarshal

var movie Movie
if err := json.Unmarshal(data, &movie); err != nil {
    log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(movie)
55

github

package github

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
)

var IssuesURL string

type IssuesSearchResult struct{}

// SearchIssues queries the GitHub issue tracker.
func SearchIssues(terms []string) (*IssuesSearchResult, error) {
    q := url.QueryEscape(strings.Join(terms, " "))
    resp, err := http.Get(IssuesURL + "?q=" + q)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        // The default HTTP client's Transport may not
        // reuse HTTP/1.x "keep-alive" TCP connections if the Body is
        // not read to completion and closed.
        io.Copy(io.Discard, resp.Body)
        return nil, fmt.Errorf("search query failed: %s", resp.Status)
    }

    var result IssuesSearchResult
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return nil, err
    }

    return &result, nil
}
56

functions

func name(parameter-list) (result-list) {
    body
}

Примеры

func hypot(x, y float64) float64 {
    return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3, 4)) // "5"

func f(i, j, k int, s, t string)                { /* ... */ }
func f(i int, j int, k int, s string, t string) { /* ... */ }
57

multiple return values

func Get(url string) (*http.Response, error) {
    // ...
    if err != nil {
        return nil, err
    }
    // ...
}

func logAndGet(url string) (*http.Response, error) {
    log.Printf("logAndGet %s", url)
    return Get(url)
}
58

named return values

package main

import (
	"fmt"
	"net/http"

	"golang.org/x/net/html"
)

func CountWordsAndImages(url string) (words, images int, err error) {
    resp, err := http.Get(url)
    if err != nil {
        return
    }
    doc, err := html.Parse(resp.Body)
    resp.Body.Close()
    if err != nil {
        err = fmt.Errorf("parsing HTML: %s", err)
        return
    }
    words, images = countWordsAndImages(doc)
    return
}

func countWordsAndImages(n *html.Node) (words, images int) {
	return
}
59

errors

resp, err := http.Get(url)
if err != nil {
    return nil, err
}

Дополнительный контекст

doc, err := html.Parse(resp.Body)
if err != nil {
    return nil, fmt.Errorf("parsing %s as HTML: %w", url, err)
}

Текст ошибки должен быть в lowercase.

genesis: crashed: no parachute: G-switch failed: bad relay orientation
60

EOF

package io

import "errors"

// EOF is the error returned by Read when no more input is available.
var EOF = errors.New("EOF")

in := bufio.NewReader(os.Stdin)
for {
    r, _, err := in.ReadRune()
    if err == io.EOF {
        break // finished reading
    }

    if err != nil {
        return fmt.Errorf("read failed: %v", err)
    }
    // ...use r...
}
61

variadic functions

func sum(vals ...int) int {
    total := 0
    for _, val := range vals {
        total += val
    }
    return total
}

fmt.Println(sum()) // "0"
fmt.Println(sum(3)) // "3"
fmt.Println(sum(1, 2, 3, 4)) // "10"

values := []int{1, 2, 3, 4}
fmt.Println(sum(values...))
fmt.Println(sum(0, values...)) // compilation error
62

function values

func Inc(i int) int { return i + 1 }

var f func(i int) int
if f == nil {
    f = Inc
}

f = func(i int) int {
    return i * 2
}
63

recursion

type Node struct {
    V    int
    L, R *Node
}

func PrintAll(w io.Writer, root *Node) {
    var visit func(n *Node)
    visit = func(n *Node) {
        fmt.Fprintln(w, n.V)
        if n.L != nil {
            visit(n.L)
        }
        if n.R != nil {
            visit(n.R)
        }
    }

    visit(root)
}
64

Thank you

Максим Иванов

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)