Pull to refresh

Передача типа map в функцию

Reading time2 min
Views14K
Недавно проскакивала статья о том, как устроены разные простые типы и слайсы в памяти. Из этой статьи мы узнали, почему переданный «по значению» слайс в функцию является передачей слайса по ссылке только до того момента, пока слайс внутри функции не потребует реаллокацию в памяти при увеличении своего capacity. Если внутри функции capacity этого слайса изменяется, и он был передан «по значению», а не в виде указателя, то слайс начинает ссылаться на совсем другой массив, совсем не тот, который будет дальше использоваться в вызывающей функции.

Такая особенность слайса может порождать «случайные» ошибки логики работы программы на этапе выполнения, если программист не учел это.

У меня возник вопрос, а нет ли похожей ситуации с типом map? Ведь у него тоже есть capacity, и он тоже может менять аллокацию в памяти при росте числа пар значений.

И я провел небольшой эксперимент, написав такой код:

package main

import (
	"fmt"
)

type myMap map[string]string

func main() {

	mymap := make(myMap, 1)
	mymap["firstKey"] = "firstValue"
	fmt.Printf("Init method nop: Address = %p Len = %d\n", &mymap, len(mymap))
	mymap.grow()
	fmt.Printf("Growed method nop: Address = %p Len = %d\n", &mymap, len(mymap))

	mymap = make(myMap, 1)
	mymap["firstKey"] = "firstValue"
	fmt.Printf("Init method p: Address = %p Len = %d\n", &mymap, len(mymap))
	(&mymap).growp()
	fmt.Printf("Growed method p: Address = %p Len = %d\n", &mymap, len(mymap))

	mymap = make(myMap, 1)
	mymap["firstKey"] = "firstValue"
	fmt.Printf("Init func nop: Address = %p Len = %d\n", &mymap, len(mymap))
	fgrow(mymap)
	fmt.Printf("Growed func nop: Address = %p Len = %d\n", &mymap, len(mymap))

	mymap = make(myMap, 1)
	mymap["firstKey"] = "firstValue"
	fmt.Printf("Init func p: Address = %p Len = %d\n", &mymap, len(mymap))
	fgrowp(&mymap)
	fmt.Printf("Growed func p: Address = %p Len = %d\n", &mymap, len(mymap))

}

func (m myMap) grow() {
	for i := 1; i < 1000000; i++ {
		m[fmt.Sprintf("nopAddKey%d", i)] = fmt.Sprintf("%d", i)
	}
}

func (m *myMap) growp() {
	for i := 1; i < 1000000; i++ {
		(*m)[fmt.Sprintf("pAddKey%d", i)] = fmt.Sprintf("%d", i)
	}
}

func fgrow(m myMap) {
	for i := 1; i < 1000000; i++ {
		m[fmt.Sprintf("nopAddKey%d", i)] = fmt.Sprintf("%d", i)
	}
}

func fgrowp(m *myMap) {
	for i := 1; i < 1000000; i++ {
		(*m)[fmt.Sprintf("pAddKey%d", i)] = fmt.Sprintf("%d", i)
	}
}

Здесь я определил два метода и две функции роста мапы, по значению и по указателю. Результатом выполнения я получил такой результат:
Init method nop: Address = 0xc042054018 Len = 1
Growed method nop: Address = 0xc042054018 Len = 1000000
Init method p: Address = 0xc042054018 Len = 1
Growed method p: Address = 0xc042054018 Len = 1000000
Init func nop: Address = 0xc042054018 Len = 1
Growed func nop: Address = 0xc042054018 Len = 1000000
Init func p: Address = 0xc042054018 Len = 1
Growed func p: Address = 0xc042054018 Len = 1000000


Итак, мы видим, что мапы вызывающей функции меняются и для случая, когда они были переданы по значению, и для случая передачи через указатель.
Tags:
Hubs:
+6
Comments20

Articles

Change theme settings