dqkelut8423 2018-07-24 05:03
浏览 32
已采纳

如果对象在切片中,则方法不会更改对象的值

Here is my program:

package main

import (
    "fmt"
)

type Number struct {
    val int
}

func (num * Number) Increment () {
    num.val += 1
}

func (num Number) Value() int {
    return num.val
}

func main() {
    numbers := []Number {
        {val: 12}, 
        {val: 7},
        {val: 0},
    }

    for _, each := range numbers {
        each.Increment()
        fmt.Println(each.Value())
    }

    for _, each := range numbers {
        fmt.Println(each.Value())
    }
}

Here is the output:

13
8
1
12
7
0

First question: why does the Increment()method not update the value in the first for loop? I used pointer as the receiver so that val can be updated for sure, but why would the second for loop print out the original values of those Numbers?

Second question: what can be done so that when I iterate over a slice of Numbers and invoke the Increment() method, all Numbers are correctly incremented?

[Edit] I noticed that if I use index-based for loop and invoke the Increment() method, values will be correctly updated. Why?

for i := 0; i < len(numbers); i++ {
    numbers[i].Increment()
}
  • 写回答

3条回答 默认 最新

  • douyinjiao9351 2018-07-24 07:46
    关注

    This for range loop:

    for _, each := range numbers {
    

    iterates over the elements of the numbers slice, and in each iteration it assigns (copies) an element to the each loop variable.

    Since your numbers slices is of type []Number, it will copy the Number struct into the each variable (whose type will be Number).

    Then you call the Number.Increment() method on this variable. Since Increment() has pointer receiver, this is a shorthand for (&each).Increment(). So the address of this loop variable is taken and used as the receiver for the Increment() method. The Increment() method will properly change this loop variable, but this is independent, distinct, detached from the slice, so you are not modifying the element in the slice.

    When you do:

    for i := 0; i < len(numbers); i++ {
        numbers[i].Increment()
    }
    

    Elements of numbers are not copied here. This:

    numbers[i].Increment()
    

    Indexes the numbers slice, and since Increment() has a pointer receiver, the address of numbers[i] is taken and used, which is the address of the element in the slice. So here, you will modify the Number struct value of the slice.

    Note that you can also use for range here:

    for i := range numbers {
        numbers[i].Increment()
    }
    

    The first iteration variable when ranging over a slice is the index.

    Also, if you would store pointers in your numbers slice (which would then have type of []*Number), the same thing would happen, but in that case the for range would copy pointers, not structs, and the pointer in the loop variable would point to the same Number struct value as the pointer in the slice would, so that would also work with your first for range variant.

    All these are detailed in Spec: For statements, in the For statements with range clause subsection.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥20 beats蓝牙耳机怎么查看日志
  • ¥15 Fluent齿轮搅油
  • ¥15 八爪鱼爬数据为什么自己停了
  • ¥15 交替优化波束形成和ris反射角使保密速率最大化
  • ¥15 树莓派与pix飞控通信
  • ¥15 自动转发微信群信息到另外一个微信群
  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏