Какие ссылочные типы данных существуют в Golang? Приведите примеры.

Ответ

В Go ссылочные типы — это типы, которые хранят ссылку (адрес) на данные в памяти, а не сами данные напрямую. Это означает, что при передаче таких типов в функции или присваивании одной переменной другой, копируется ссылка, а не весь объем данных. Изменения, сделанные через одну ссылку, будут видны через другую.

К ним относятся:

  1. Указатели (*T) — хранят адрес переменной в памяти. Позволяют напрямую изменять значение по адресу.

    x := 10
    p := &x // p теперь указатель на x
    *p = 20 // Изменяем значение по адресу, на который указывает p
    fmt.Println(x) // Вывод: 20
  2. Срезы ([]T) — это дескрипторы, которые ссылаются на базовый массив. Они содержат указатель на первый элемент, длину и емкость.

    a := []int{1, 2, 3}
    b := a // b теперь ссылается на тот же базовый массив, что и a
    b[0] = 99
    fmt.Println(a) // Вывод: [99 2 3] (изменение в b повлияло на a)
  3. Мапы (map[K]V) — это хэш-таблицы, которые управляются через указатель на внутреннюю структуру данных. При передаче мапы, передается ссылка на эту структуру.

    m := make(map[string]int)
    m["one"] = 1
    n := m // n ссылается на ту же мапу, что и m
    n["two"] = 2
    fmt.Println(m) // Вывод: map[one:1 two:2]
  4. Каналы (chan T) — используются для безопасного обмена данными между горутинами. Каналы являются ссылочными типами, и при их передаче передается ссылка на структуру канала.

    ch := make(chan int)
    go func() {
    ch <- 42
    }()
    val := <-ch
    fmt.Println(val) // Вывод: 42
  5. Функции — в Go функции являются первоклассными гражданами и могут быть присвоены переменным, переданы как аргументы и возвращены из других функций. Когда вы передаете функцию, вы передаете ссылку на ее исполняемый код.

    add := func(a, b int) int { return a + b }
    operate := func(f func(int, int) int, x, y int) int {
    return f(x, y)
    }
    result := operate(add, 5, 3)
    fmt.Println(result) // Вывод: 8
  6. Интерфейсы — значение интерфейса состоит из двух компонентов: указателя на тип и указателя на значение. Когда вы присваиваете конкретное значение интерфейсу, оно хранится по ссылке.

    
    type Greeter interface {
    Greet() string
    }

type Person struct { Name string } func (p Person) Greet() string { return "Hello, " + p.Name }

var g Greeter p := Person{"Alice"} g = p // g теперь содержит ссылку на p и его тип fmt.Println(g.Greet()) // Вывод: Hello, Alice