Узнай разницу, чтобы не совершать дорогостоящих ошибок

Вступление

Многие языки программирования поддерживают передачу аргумента по значению и / или по ссылке. В этой статье мы узнаем, как функция Go обрабатывает переданные аргументы.

Что такое передача по ценности?

В Go, когда параметр передается функции по значению, это означает, что параметр копируется в другое место вашей памяти. При доступе или изменении переменной в вашей функции, только копия доступна или изменена - исходное значение никогда не изменяется. Все примитивные / базовые типы (int и его варианты, float и его варианты, логические, строковые, массивы и структуры) в Go передаются по значению. Передача по значению - это обычно то, как ваши значения передаются функциям. Давайте посмотрим на несколько примеров:

Если вы просмотрите приведенные выше примеры, вы можете убедиться, что значения переменных, переданных функциям, остаются неизменными до и после вызовов функций. Короче говоря, переменные передавались по значению.

Теперь давайте рассмотрим другой способ обработки параметров функцией Go - передачу по ссылке.

Что такое передача по ссылке?

Существует понимание / дискуссия о том, передаются ли составные типы Go в функцию по ссылке. Если быть точным, Go никоим образом не поддерживает семантику «передачи по ссылке». Причина проста; Go не поддерживает ссылочную переменную, как в других языках программирования, таких как C ++. По идее, в случае Map, когда вы создаете тип Map и затем передаете его функции, если функция изменяет аргумент, эффект также повлияет на исходную переменную. Может показаться, что переменная Map передается по ссылке, но это неверно. Когда вы создаете переменную типа Map с помощью «make ()» под капотом, она вызывает makemap (), которая возвращает * hmap (то есть указатель). Итак, передача переменной функции - это передача по указателю, а не по ссылке. Та же концепция применима к каналу. Хотя Slice относительно отличается по структуре данных (структура с тремя типами; указатель на базовый массив, длина среза и емкость среза). Но он также рассматривается как переход по указателю.

Прежде чем мы перейдем к некоторым примерам того, как функция Go обрабатывает составные типы (срез и карта), канал, указатель и функцию, давайте взглянем на этот фрагмент кода, который подтверждает, что составные типы Go не проходят мимо. Ссылка:

package main
import "fmt"

func myMap(v map[int]int) {
    v = make(map[int]int) // make() declares and initializes v to 0
}

func myInt(v []int) {
    v = make([]int) // make() declares and initializes v to 0
}
func main() {
    
   //v is declared but NOT initialized, which means its value is nil
     var v map[int]int
     myMap(v)   
     fmt.Println(v == nil) // true
   //i is declared but NOT initialized, which means its value is nil
     var i []int
     myInt(i)
     fmt.Println(i == nil) // true
}

Глядя на приведенный выше пример, мы можем сказать, что даже после объявления переменной V и последующего вызова myF () для нее, чтобы инициализировать ее значением 0. В конце концов, когда мы проверяем ее значение после вызова, это приводит к правда'. Это означает, что myF () не рассматривает «v как передачу по ссылке (потому что Go не поддерживает эту семантику). Тот же результат, который мы получили бы, если бы попробовали Slice и Channel. Вы можете прочитать больше по этой теме в ВЕЛИКОЛЕПНОМ блоге Дэйва Чейни.

Ниже приведен пример передачи составных и других типов (помимо описанных выше примитивных типов) в Go:

Просматривая приведенные выше примеры, мы можем увидеть эффект передачи параметров функциям. Например, в slice мы можем подтвердить, что значение переменной coffeeBox было изменено при передаче в функцию modifySlice. То же самое с map, pointer, function и channel.

Если вам нужно изменить значение базового типа (int, float, bool и т. Д.), Просто передайте адрес памяти переменной в функцию (другими словами, рассматривайте параметр как указатель). Раздел указателя ясно иллюстрирует этот сценарий.

Резюме

Go поддерживает сематику Pass-By-Value; когда аргументы передаются по значению, функция получает копию каждого аргумента - изменения копии не влияют на вызывающую сторону. С другой стороны, он не поддерживает передачу по ссылке. Но он поддерживает Pass-by-Pointer, который можно использовать для изменения значения базового аргумента.

В этой короткой статье мы рассмотрели два способа, которыми Go обрабатывает параметры, переданные его функции. Важно знать об этой концепции, чтобы избежать ложных / неправильных ожиданий.

До следующей статьи, продолжайте!