Базовые конструкции языка
Лекция 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
Константы
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
var, const, type, funcОбщая форма
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
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
x := 1 p := &x // p, of type *int, points to x fmt.Println(*p) // "1" *p = 2 fmt.Println(x)
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() } }
new(T) создаёт новую переменную с типом *T.
func newInt() *int {
return new(int)
}
func newInt() *int {
var dummy int
return &dummy
}...
if (full_data)
{
auto value = getPointsData();
full_points_data = &value;
}
auto table = getPointsByDocument(doc_id, full_points_data);
...Что не так?
10var p = f()
func f() *int {
v := 1
return &v
}Память освобождается, после того как переменная становится недостижимой.
Компилятор может переместить переменную со стека на кучу.
var global *int
func f() {
var x int
x = 1
global = &x
}И с кучи на стек.
func g() {
y := new(int)
*y = 1
}Алгоритм для определения стек или куча для переменной
12type 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
.go файловpackage example const ( C0 = 9.8 c1 = 15 ) var ( V0 string v1 string ) func F0() {} func f1() {} type ( T0 int t1 string )
tempconv/types.gopackage 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) }
tempconv/conv.gopackage 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))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 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))]) }
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 }
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) } }
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 ff, err := os.Open(fname)
if err != nil {
return err
}
f.ReadByte()
f.Close()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)
}
string - неизменяемая последовательность байт.
s[i] - обращается к i-тому байту (не символу).
var s = "hello" var doc = `Go is a tool for managing Go source code. Usage: go command [arguments] ... `
Символы кодируются числами.
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"
}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 '界' 30028runes := []rune("Hello, 世界")
s := string(runes)
fmt.Println(s) // Hello, 世界
Некорректный байт превращается unicode replacement character '\uFFFD'.
string([]rune(s)) быть больше s?string([]rune(s)) быть больше s?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" }
strings - HasSuffix, Split, Join, etc.bytes - аналог strings для []byte. unicode - IsDigit, IsLetter.strconv - конвертация между строкой и int, float.path - работа с unix путямиfilepath - работа с путями текущей платформыs := "abc" b := []byte(s) s2 := string(b) fmt.Println(s == s2) // true
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()
}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
)const (
_ = 1 << (10 * iota)
KiB
MiB
GiB
TiB // (exceeds 1 << 32)
PiB
EiB
ZiB // (exceeds 1 << 64)
YiB
)fmt.Println(YiB/ZiB) // "1024"
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{}
}data, len, cap.s := make([]int, 10) s = s[:0] s = s[:10] s = s[:5:5] s = s[:10] // panic
a := [10]int{}
s := a[:]// 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]
}
}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) == 0make([]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 = append(stack, v) // push v top := stack[len(stack)-1] // top of stack stack = stack[:len(stack)-1] // pop
queue = append(queue, v) // push v front := queue[0] // front of queue queue = queue[1:] // pop
func remove(slice []int, i int) []int {
copy(slice[i:], slice[i+1:])
return slice[:len(slice)-1]
}ages := make(map[string]int)
ages := map[string]int{
"alice": 31,
"charlie": 34,
}
ages := make(map[string]int)
ages["alice"] = 31
ages["charlie"] = 34ages["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)
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 // panicage, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }
if age, ok := ages["bob"]; !ok { /* ... */ }var s0 map[string]bool
var s1 map[string]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 codetype tree struct {
value int
left, right *tree
}type tree struct {
value int
left, right *tree
}type Point struct{ X, Y int }
p := Point{1, 2}
p := Point{X: 1, Y: 2}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}]++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)) // 0type Point struct {
X, Y int
}
type Circle struct {
Point
Radius int
}
c := Circle{
Point: Point{X: 10, Y: 10},
Radius: 1,
}
c.X = 0type 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)var movie Movie
if err := json.Unmarshal(data, &movie); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(movie)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 }
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) { /* ... */ }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)
}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
}
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
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...
}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 errorfunc 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
}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)
}Максим Иванов