使用指针在go中按预期工作,编辑结构列表变量

noh*_*hup 5 methods json struct pointers go

我有一个看起来像的结构

type Request struct {
    Name string `json:"name"`
    Parameters []Parameter `json:"parameters"`
}
Run Code Online (Sandbox Code Playgroud)

type Parameter struct {
    Attached bool `json:"attached"`
    Script string `json:"script"`
}
Run Code Online (Sandbox Code Playgroud)

现在,我已经将json解组到struct中,而Script变量的http位置为" http://localhost/helloworld.sh ".我想要做的是,Parameter.Scripthttp://localhost/helloworld.sh脚本的实际内容更改struct变量,这是一个简单的ascii shell脚本.我为内部结构编写了一个方法

func (p *Parameter) SetScript(script string)  {
    p.Script = script
}
Run Code Online (Sandbox Code Playgroud)

使用指针Parameter,

GetScript函数中,尝试在获取响应主体后调用该方法.

func GetScript(params *Request)  {
  for _, i := range params.Parameters {
    switch i.Attached {
    case false:
        client := new(http.Client)
        req, _ := http.NewRequest("GET", i.Script, nil)
        resp, _ := client.Do(req)
        defer resp.Body.Close()
        reader, _ := ioutil.ReadAll(resp.Body)
        i.SetScript(string(reader))
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我在调用此函数后打印结构时,它没有修改变量,http://localhost/helloworld.sh只打印了变量.我能够得到响应体,这是脚本的实际内容,但我无法从GetScript函数中替换struct变量.有人可以指出正确的方法吗?

谢谢.

icz*_*cza 4

问题是您正在使用循环for _, i := range,并且修改了循环内的循环变量:

for _, i := range params.Parameters {
    switch i.Attached {
    case false:
        // ...
        i.SetScript(string(reader))
    }
}
Run Code Online (Sandbox Code Playgroud)

循环变量i是您范围内的切片元素的副本。因此,如果您对其进行任何修改,您只会修改副本而不会修改切片中的元素。(请注意,该方法确实有一个指针接收器,但它将接收副本SetScript()的地址,因此它可以并且只会修改副本。)

一种解决方法是使用仅索引range,并使用索引引用切片元素(将所有出现的 替换为iparams.Parameters[i]

for i := range params.Parameters {
    switch params.Parameters[i].Attached {
    case false:
        // ...
        params.Parameters[i].SetScript(string(reader))
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以通过将切片分配给局部变量来简化上面的代码(这将仅复制切片头而不是其元素,并且它将引用相同的底层数组),并使用if语句而不是那个丑陋的switch

p := params.Parameters
for i := range p {
    if !p[i].Attached {
        // ...
        p[i].SetScript(string(reader))
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种简化/改进是获取索引表达式的地址并使用它(这样您就可以省略多次重复):

for i := range params.Parameters {
    p := &params.Parameters[i]
    if !p.Attached {
        // ...
        p.SetScript(string(reader))
    }
}
Run Code Online (Sandbox Code Playgroud)