В go указатели поддерживаются ограниченно - в частности, не поддерживается арифметика указателей.
В go нет free() или delete(), но есть гарбаж-коллектор.
В go переменные как правило хранят значения, но могут хранить и ссылки - как в случае с каналами,
функциями, методами, мапами и слайсами.
Значения, передаваемые в функции и методы, копируются, т.е. создаются заново - это справедливо для чисел и строк,
которые immutable. Это справедливо также для массивов и структур.
Слайсы и мапы же передаются по ссылке, поскольку являются референсными типами.
Указатель в go - это переменная, которая хранит адрес другой переменной.
& - это ссылка, равная этому адресу памяти.
Следующий рисунок иллюстрирует вышесказанное:
В go указатель может указывать на другой указатель.
Пример
z := 37 // z is of type int
pi := &z // pi is of type *int (pointer to int)
ppi := &pi // ppi is of type **int (pointer to pointer to int)
fmt.Println(z, *pi, **ppi)
**ppi++ // то же самое, что: (*(*ppi))++ или: *(*ppi)++
fmt.Println(z, *pi, **ppi)
Вывод:
37 37 37
38 38 38
Еще пример:
i := 9
j := 5
product := 0
swapAndProduct1(&i, &j, &product)
fmt.Println(i, j, product)
func swapAndProduct1(x, y, product *int) {
if *x > *y {
*x, *y = *y, *x
}
*product = *x * *y // The compiler would be }
}
Вывод:
5 9 45
Последняя функция может быть переписана более понятным языком, но у нее будет минус -
она своп делает не по месту, а создает для этого локальные переменные:
func swapAndProduct2(x, y int) (int, int, int) {
if x > y {
x, y = y, x
}
return x, y, x * y
}
Для того чтобы в go создать переменную-указатель и присвоить ей значение, есть два варианта -
обычный стандартный и с помощью оператора new() . Рассмотрим пример со структурой:
type composer struct {
name string
birthYear int
}
antónio := composer{"António Teixeira", 1707} // composer value
agnes := new(composer) // pointer to composer
agnes.name, agnes.birthYear = "Agnes Zimmermann", 1845
julia := &composer{} // pointer to composer
julia.name, julia.birthYear = "Julia Ward Howe", 1819
augusta := &composer{"Augusta Holmès", 1847} // pointer to composer
fmt.Println(antónio)
fmt.Println(agnes, augusta, julia)
Вывод:
{António Teixeira 1707}
&{Agnes Zimmermann 1845} &{Augusta Holmès 1847} &{Julia Ward Howe 1819}
Следующий пример показывает, что когда в функцию передаются референсные типы, такие, как слайсы и мапы,
все изменения, которые с ними происходят внутри функции, будут видны снаружи:
Если же мы передадим в функцию структуру, изменения внутри функции снаружи видны не будут, поскольку
структура - это value-тип. Но если мы передадим в функцию ссылку на структуру, то будут:
Слайс в go - это коллекция переменного размера.
У него есть функция append().
Хранимые обьекты по умолчанию должны быть одного типа, но нам никто не мешает создать
массив или слайс пустых интерфейсов, и можно будет хранить обьекты произвольного типа,
Проблемы будут на этапе извлечения элементов, Слайсы можно создавать двумя способами -
стандартным и встроенной функцией make(), с помощью которой кстати также можно создавать мапы и каналы.
У слайса есть две встроенных функции - len() и cap().
cap() отличается от len() тем, что равен максимально возможному числу элементов.
Пример:
s := []string{"A", "B", "C", "D", "E", "F", "G"}
t := s[2:6]
fmt.Println(t, s, "=", s[:4], "+", s[4:])
s[3] = "x"
t[len(t)-1] = "y"
fmt.Println(t, s, "=", s[:4], "+", s[4:])
Вывод:
[C D E F] [A B C D E F G] = [A B C D] + [E F G]
[C x E y] [A B C x E y G] = [A B C x] + [E y G]
s := []string{"A", "B", "C", "D", "E", "F", "G"}
t := []string{"K", "L", "M", "N"}
u := []string{"m", "n", "o", "p", "q", "r"}
s = append(s, "h", "i", "j") // Append individual values
s = append(s, t...) // Append all of a slice's values
s = append(s, u[2:5]...) // Append a subslice
b := []byte{'U', 'V'}
letters := "wxy"
b = append(b, letters...) // Append a string's bytes to a byte slice
fmt.Printf("%v\n%s\n", s, b)
Вывод:
[A B C D E F G h i j K L M N o p q]
UVwxy
Для того, чтобы вставить элемент в произвольное место слайса, прийдется писать функцию:
s := []string{"M", "N", "O", "P", "Q", "R"}
x := InsertStringSliceCopy(s, []string{"a", "b", "c"}, 0) // At the front
y := InsertStringSliceCopy(s, []string{"x", "y"}, 3) // In the middle
z := InsertStringSliceCopy(s, []string{"z"}, len(s)) // At the end
fmt.Printf("%v\n%v\n%v\n%v\n", s, x, y, z)
func InsertStringSliceCopy(slice, insertion []string, index int) []string {
result := make([]string, len(slice)+len(insertion))
at := copy(result, slice[:index])
at += copy(result[at:], insertion)
copy(result[at:], slice[index:])
return result
}
Вывод:
[M N O P Q R]
[a b c M N O P Q R]
[M N O x y P Q R]
[M N O P Q R z]
Функция вставки может иметь следующий вид - ее отличие от предыдущего варианта в том,
что она меняет существующий слайс, в то время как предыдущая создает новый:
Можно удалять элементы из слайса с произвольной позиции:
s := []string{"A", "B", "C", "D", "E", "F", "G"}
s = s[2:] // Remove s[:2] from the front
fmt.Println(s)
Вывод:
[C D E F G]
s := []string{"A", "B", "C", "D", "E", "F", "G"}
s = s[:4] // Remove s[4:] from the end
fmt.Println(s)
Вывод:
[A B C D]
s := []string{"A", "B", "C", "D", "E", "F", "G"}
s = append(s[:1], s[5:]...) // Remove s[1:5] from the middle
fmt.Println(s)
Вывод:
[A F G]
Стандартная библиотека содержит функции сортировки слайсов, состоящих из чисел или строк.
Найти в слайсе индекс по значению можно без всяких функций:
files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"}
target := "Makefile"
for i, file := range files {
if file == target {
fmt.Printf("found \"%s\" at files[%d]\n", file, i)
break
}
}
Вывод:
found "Makefile" at files[2]
С использованием стандартных поисковых функций:
sort.Strings(files)
fmt.Printf("%q\n", files)
i := sort.Search(len(files),
func(i int) bool { return files[i] >= target })
if i < len(files) && files[i] == target {
fmt.Printf("found \"%s\" at files[%d]\n", files[i], i)
}
Вывод:
["Makefile" "Test.conf" "main.go" "misc.go" "util.go"]
found "Makefile" at files[0]
Map - неотсортированная коллекция пар ключ-значение не-фиксированного размера.
В качестве ключа используются встроенные типы. Мапы, или словари - это ссылочный тип.
Поиск по ключу в мапах шустрее линейного поиска.
Ключи в мапах должны быть одного типа, равно как и значения.
Операции над мапами:
m[k] = v
delete(m, k)
v := m[k]
v, found := m[k]
len(m)
populationForCity := map[string]int{"Istanbul": 12610000,
"Karachi": 10620000, "Mumbai": 12690000, "Shanghai": 13680000}
for city, population := range populationForCity {
fmt.Printf("%-10s %8d\n", city, population)
}
Вывод:
Shanghai 13680000
Mumbai 12690000
Istanbul 12610000
Karachi 10620000
Для того чтобы вывести значения мапа в алфавитном порядке, нужно скопировать мап в слайс,
потом отсортировать слайс:
cities := make([]string, 0, len(populationForCity))
for city := range populationForCity {
cities = append(cities, city)
}
sort.Strings(cities)
for _, city := range cities {
fmt.Printf("%-10s %8d\n", city, populationForCity[city])
}
Вывод:
Beijing 11290000
Istanbul 12610000
Karachi 11620000
Mumbai 12690000