Неявные ссылки (указатели) в Go

Решаю простые задачки на CodeWars. Возникла проблема со следующей частью кода:

func FindOutlier(integers []int) int {
    coping := integers //делаем копию массива
    var DosZeroCount int = 0
    var DosZeroNotCount int = 0
    for index, _ := range coping {
        coping[index] = coping[index] % 2
        if coping[index] == 1 {
            DosZeroNotCount++
        } else {
            DosZeroCount++
        }
    }//обнуляется integers без одного элемента

Проблема заключается в том, что coping - получается неявной ссылкой исходного массива. Согласно описанию языка такое действие coping := integers должно полностью копировать массив, а не создавать ссылку на него. Каким образом можно скопировать массив полностью? Подозреваю, что дело в том, что integers - аргумент функции...


Ответы (1 шт):

Автор решения: Senior Pomidor

работа с массивами и map-ами отличается и нужно знать несколько правил.

coping := integers работает внутри range, когда вы меняете состояние элемента, но не хотите, чтобы в map-е или в массиве оно менялось.

    m := map[string]int{"a": 1, "b": 2, "c": 3}
    for k, v := range m {
        copyOfValue := v
        copyOfValue = 10
        v = 11
        fmt.Printf("copyOfValue: %s: %d\n", k, copyOfValue)
        break
    }

    fmt.Printf("m: %v\n", m)

copyOfValue: c: 10
m: map[a:1 b:2 c:3]

for i := range a и for i, v := range &a никогда не создают копию a, но вот for i, v := range a создает копию)) просто нужно запомнить. Вот убедитесь сами

func IndexArray() {
    a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}

    for i := range a {
        a[3] = 100
        if i == 3 {
            fmt.Println("IndexArray", i, a[i])
        }
    }
}

func IndexValueArray() {
    a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}

    for i, v := range a {
        a[3] = 100
        if i == 3 {
            fmt.Println("IndexValueArray", i, v)
        }
    }
}

func IndexValueArrayPtr() {
    a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}

    for i, v := range &a {
        a[3] = 100
        if i == 3 {
            fmt.Println("IndexValueArrayPtr", i, v)
        }
    }
}

func main() {
    IndexArray()
    IndexValueArray()
    IndexValueArrayPtr()
}

IndexArray 3 100
IndexValueArray 3 4
IndexValueArrayPtr 3 100

ссылка чтобы поиграться

поэтому, самый простой способ сделать копию массива - это

    src := []int{1, 2, 3, 4, 5}
    dst := make([]int, len(src))
    numberOfElementsCopied := copy(dst, src)

теперь поговорит про слайсы(slice)

В Golang есть понятия slice - это почти как массив, только ведет себя не как массив. Например, в следующем коде будет копия

arr1:=[...]int{1,2,3}
arr2:=arr1
arr1[1]=99
fmt.Println(arr1)
fmt.Println(arr2)

[1 99 3]
[1 2 3]

но если "копирвоание" изменить так, то изменится и исходный массив

arr1:=[...]int{1,2,3}
arr2:=arr1[:]
arr1[1]=99
fmt.Println(arr1)
fmt.Println(arr2)

[1 99 3]
[1 99 3]

А все потому, что слайс - это указатель на массив(точнее каждый элемент нового слайся ссылается на элемент исходного).

Slicing does not copy the slice's data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices. Therefore, modifying the elements (not the slice itself) of a re-slice modifies the elements of the original slice

Вот источник

Итого, чтобы не запутались

array := [5]int{1, 2, 3, 4, 5} // это массив
slice := array[:]              // это срез массива
slice := [...]int{1, 2, 3, 4, 5} // это срез массива
slice := int{1, 2, 3, 4, 5} // это срез массива
→ Ссылка