shad-go/lectures/01-basics/lecture.slide

734 lines
14 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Базовые конструкции языка
Лекция 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
* Predeclated 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
var true = false
* Exported identifiers
Регистр первой буквы определяет видимость имени за пределами пакета
.play exported/example.go
Пакеты всегда называют в нижнем регистре и без подчеркиваний
fmt
grpcmiddleware
* Declaration
- 4 типы объявлений `var`, `const`, `type`, `func`
- Пакет состоит из множества `.go` файлов
- Порядок объявлений в коде не важен
* 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
* 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
* pointers
x := 1
p := &x // p, of type *int, points to x
fmt.Println(*p) // "1"
*p = 2
fmt.Println(x)
* escape analysis
var p = f()
func f() *int {
v := 1
return &v
}
* flag
.play flag/main.go
* new
`new(T)` создаёт новую переменную с типом `*T`.
func newInt() *int {
return new(int)
}
func newInt() *int {
var dummy int
return &dummy
}
* 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
* variable lifetime
Память освобождается, после того как переменая становится недостижимой.
Компилятор может переместить переменную со стека на кучу.
var global *int
func f() {
var x int
x = 1
global = &x
}
И с кучи на стек.
func g() {
y := new(int)
*y = 1
}
.link https://habr.com/ru/companies/oleg-bunin/articles/676332/ Алгоритм для определения стек или куча для переменной
* type declaration
type name underlying-type
.play tempconv0/conv.go
- type alias
type name = another-type
* packages
- Файл `tempconv/types.go`
.play tempconv/tempconv.go
- Файл `tempconv/conv.go`
.play tempconv/conv.go
* packages
fmt.Printf("Brrrr! %v\n", tempconv.AbsoluteZeroC)
fmt.Println(tempconv.CToF(tempconv.BoilingC))
* 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 }
* package initialization
.play popcount/popcount.go
* scope
.play scope/scope.go
* scope
.play scope/if.go
* 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()
* 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)
}
* strings
`string` - неизменяемая последовательность байт.
`s[i]` - обращается к i-тому байту (не символу).
var s = "hello"
var doc = `Go is a tool for managing Go source code.
Usage:
go command [arguments]
...
`
* unicode
Символы кодируются числами.
type rune = int32
Кодировка utf8
0xxxxxxx runes 0127
110xxxxx 10xxxxxx 1282047
1110xxxx 10xxxxxx 10xxxxxx 204865535
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 655360x10ffff
Разница между рунами и байтами
import "unicode/utf8"
func countRunes() {
s := "Hello, 世界"
fmt.Println(len(s)) // "13"
fmt.Println(utf8.RuneCountInString(s)) // "9"
}
* 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)
}
runes := []rune("Hello, 世界")
s := string(runes)
Некорректный байт превращается _unicode_replacement_character_ `'\uFFFD'`.
- Может ли строка `string([]rune(s))` быть больше `s`?
* stdlib
- `strings` - HasSuffix, Split, Join, etc.
- `bytes` - аналог `strings` для `[]byte`.
- `unicode` - IsDigit, IsLetter.
- `strconv` - конвертация между строкой и `int`, `float`.
- `path` - работа с unix путями
- `filepath` - работа с путями текущей платформы
* []byte
s := "abc"
b := []byte(s)
s2 := string(b)
* 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()
}
* 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
)
* untyped constants
const (
_ = 1 << (10 * iota)
KiB
MiB
GiB
TiB // (exceeds 1 << 32)
PiB
EiB
ZiB // (exceeds 1 << 64)
YiB
)
- Компилятор гарантирует как минимум 256 бит точности
fmt.Println(YiB/ZiB) // "1024"
* arrays
var a [3]int
var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}
d := [...]int{1, 2, 3}
h := [...]int{99: -1}
- arrays are values
func zero(ptr *[32]byte) {
*ptr = [32]byte{}
}
* slices
- slice это тройка `data`, `len`, `cap`.
s := make([]int, 10)
s = s[:0]
s = s[:10]
s = s[:5:5]
s = s[:10] // panic
- make эквивалентен следующему коду
a := [10]int{}
s := a[:]
* 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]
}
}
* 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
* 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
* stack
stack = append(stack, v) // push v
top := stack[len(stack)-1] // top of stack
stack = stack[:len(stack)-1] // pop
* queue
queue = append(queue, v) // push v
front := queue[0] // front of queue
queue = queue[1:] // pop
- O(1) амортизированное время всех операций
- Не очень эффективно по числу аллокаций
* copy
func remove(slice []int, i int) []int {
copy(slice[i:], slice[i+1:])
return slice[:len(slice)-1]
}
* 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
* 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)
* maps
for name, age := range ages {
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
* maps
age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }
if age, ok := ages["bob"]; !ok { /* ... */ }
* set
var s0 map[string]bool
var s1 map[string]struct{}
* 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
* struct
type tree struct {
value int
left, right *tree
}
* struct
type Point struct{ X, Y int }
p := Point{1, 2}
p := Point{X: 1, Y: 2}
* 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}]++
* 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
* json
type Movie struct {
Title string
Year int `json:"year"`
Color bool `json:"color,omitempty"`
Actors []string
}
- marshal
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)
* unmarshal
var movie Movie
if err := json.Unmarshal(data, &movie); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(movie)
* github
.play github/github.go /func Search/,/^}/
* 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) { /* ... */ }
* 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)
}
* named return values
.play countwords/main.go /func Count/,/^}/
* 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
* 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...
}
* 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
* 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
}
* 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)
}