Go (или Golang) — это современный статически типизированный компилируемый язык программирования, предназначенный для создания масштабируемого, параллельного и эффективного программного обеспечения. Он поставляется с различными встроенными функциями и возможностями, которые помогают разработчикам писать краткий и производительный код. Среди этих функций есть new() и make(), которые на первый взгляд могут показаться похожими, но служат разным целям и имеют решающее значение для распределения памяти и инициализации данных в Go.

В этом сообщении блога мы рассмотрим различия между функциями new() и make() и поймем, когда и как их эффективно использовать.

new() и make() Функции

И new(), и make() — встроенные функции Go, используемые для распределения памяти. Однако они используются для разных типов данных и сценариев:

new() функция:

  • new() используется для выделения памяти для типов значений (например, целых чисел, чисел с плавающей запятой, структур) и возвращает указатель на вновь выделенное обнуленное значение.
  • Он принимает один аргумент, который является типом, и возвращает указатель на этот тип.

функция make():

  • make() используется для создания и инициализации срезов, карт и каналов, которые в Go являются ссылочными типами.
  • Он принимает два или три аргумента, в зависимости от типа, и возвращает инициализированное (не обнуленное) значение, готовое к использованию.

Понимание функции new()

Синтаксис функции new() прост, как показано ниже.

func new(Type) *Type

Здесь Type представляет тип значения, для которого мы хотим выделить память. Давайте посмотрим пример использования new().

В этом примере мы создаем новый экземпляр структуры Person, используя new(), а затем присваиваем значения его полям, используя указатель.

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    // Using new() to allocate memory for a Person struct
    p := new(Person)

    fmt.Printf("%T\n", p)

    // Accessing struct fields using the pointer
    p.Name = "Alice"
    p.Age = 30

    // Displaying the values
    fmt.Println("Name:", p.Name)
    fmt.Println("Age:", p.Age)
}

Эта программа выдаст результат, как показано ниже.

> go run main.go
*main.Person
Name: Alice
Age: 30

Понимание make() функции

Синтаксис функции make() варьируется в зависимости от типа, с которым она используется.

Для фрагментов

func make([]Type, len, cap) []Type
  • Type: Тип элементов, которые будет содержать срез.
  • len: Начальная длина среза.
  • cap: емкость среза, которая не является обязательной и используется для указания емкости базового массива. Если оно не указано, по умолчанию оно принимает то же значение, что и длина.

Пример создания среза с помощью make():

package main

import "fmt"

func main() {
    // Using make() to create a slice of integers
    numbers := make([]int, 5, 10)

    // Displaying the slice's length, capacity, and values
    fmt.Println("Length:", len(numbers))
    fmt.Println("Capacity:", cap(numbers))
    fmt.Println("Values:", numbers)

    // Using make() to create a slice of integers
    numbersWithoutOptional := make([]int, 5)

    // Displaying the slice's length, capacity, and values
    fmt.Println("Length:", len(numbersWithoutOptional))
    fmt.Println("Capacity:", cap(numbersWithoutOptional))
    fmt.Println("Values:", numbersWithoutOptional)
}

Эта программа выдаст результат, как показано ниже.

> go run main.go
Length: 5
Capacity: 10
Values: [0 0 0 0 0]
Length: 5
Capacity: 5
Values: [0 0 0 0 0]

Для карт

func make(map[KeyType]ValueType, initialCapacity int) map[KeyType]ValueType
  • KeyType: Тип ключей на карте.
  • ValueType: Тип значений, связанных с ключами.
  • initialCapacity: Начальная вместимость карты. Это необязательно, но его можно использовать для оптимизации производительности, если количество элементов известно заранее.

Пример создания карты с помощью make()

package main

import "fmt"

func main() {
    // Using make() to create a map of string keys and int values
    scores := make(map[string]int)
    // Adding values to the map
    scores["Alice"] = 95
    scores["Bob"] = 87
    // Displaying the map
    fmt.Println("Scores:", scores)
}
> go run main.go
Scores: map[Alice:95 Bob:87]

Для каналов

func make(chan Type, capacity int) chan Type
  • Type: Тип значений, которые можно отправлять и получать через канал.
  • capacity: Размер буфера канала. Если установлено значение 0, канал не буферизуется.

Пример создания канала с помощью make()

package main

import (
    "fmt"
    "time"
)

func main() {
    // Using make() to create an unbuffered channel of integers
    ch := make(chan int)
    // Sending data into the channel using a goroutine
    go func() {
        for i := 1; i <= 5; i++ {
            ch <- i
            time.Sleep(time.Second) // Simulating some work before sending the next value
        }
        close(ch)
    }()
    // Receiving data from the channel
    for num := range ch {
        fmt.Println("Received:", num)
    }
}
> go run main.go
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5

Заключение

В этом сообщении блога мы раскрыли тайну функций new() и make() в Go и объяснили их различия и варианты использования. Обобщить:

  • Используйте new(), чтобы выделить память для типов значений и получить указатель на обнуленное значение.
  • Используйте make() для создания и инициализации фрагментов, карт и каналов (ссылочных типов) с соответствующими типами и исходными возможностями.

Понимание различий между new() и make() имеет решающее значение для эффективного распределения памяти и инициализации данных в Go. Правильное применение этих функций приведет к более чистому и оптимизированному коду в ваших проектах Golang. Приятного кодирования!

Первоначально опубликовано для https://thebugshots.dev