【问题标题】:Change value of pointered argument inside a function更改函数内指针参数的值
【发布时间】:2021-08-15 17:28:30
【问题描述】:

我被困在 Go 中似乎/应该很容易的事情上。

我写了一个小的 go playground 来更容易解释我的问题 => https://play.golang.org/p/Sm0SzrvEZS_o

    package main

    import (
        "github.com/sirupsen/logrus"
    )

    type toto struct {
        name string
    }

    func transform (data ...interface{}) {
        logrus.Info("data before ", data)
        
        data[0] = "tutu"
        
        logrus.Info("data after ", data)
    }

    func main() {
        var titi toto
    
        logrus.Info("titi before ", titi) // -> empty
    
        transform(&titi)
    
        logrus.Info("titi after ", titi) // -> should have a name but don't
    }

目标是将结构传递给函数,在其中进行修改并继续在调用函数中使用它。可悲的是,参数在子函数内部被修改,但没有移动到调用者中。

我是这门语言的初学者,也许我只是在某个地方遗漏了一些东西......非常感谢您的帮助

【问题讨论】:

  • A minimal reproducible example 应该包含在问题本身中。您不是在函数中修改titi,而是在修改data。您没有为 name 字段分配任何内容,因此无法更改 name
  • 这就是重点,我不明白如何修改titi。也许我表达错误了……
  • 将代码添加到问题中。对不起
  • 我将从 Tour of Go 开始介绍语言基础知识(然后是 Effective Go 和规范)。您必须为该字段分配一些内容才能对其进行修改:play.golang.org/p/FrVBPPlllC_9
  • 完成,但不明白这一点。非常感谢您的工作示例^^

标签: go pointers syntax arguments


【解决方案1】:

在 Go 中,在函数中作为参数传递的变量实际上是变量的副本,而不是实际变量本身。如果要修改传入的变量,需要将其作为指针传入。

就个人而言,每当我创建一个接受结构的函数时,我都会将函数设置为接受指向该结构实例的指针。这样做的好处是内存效率更高(因为我的程序不必在每次调用函数时都创建变量的副本),并且它允许我修改我传递的结构的实例。

我会这样做:

package main

import (
    "github.com/sirupsen/logrus"
)

type toto struct {
    name string
}

func transform (t *toto) {
    logrus.Info("t before: ", t)
    
    // since t is a pointer to a toto struct, 
    // I can directly assign "tutu" to the "name" field 
    // using the "dot" operator 
    t.name = "tutu"
    
    logrus.Info("t after: ", t)
}

func main() {
    // init a pointer to a toto instance
    titi := &toto{}
    
    logrus.Info("titi before: ", titi) // -> empty
    
    // this works because transform() accepts a pointer
    // to a toto struct and and titi is a pointer to a toto instance
    transform(titi)
    
    logrus.Info("titi after (as a pointer): ", titi) // -> not empty
    logrus.Info("titi after (as a value): ", *titi) // -> also not empty
}

【讨论】:

  • 可悲的是,我的问题是关于我为来自 lib 的测试而模拟的一个函数,我无法修改 function 的定义。但是感谢您对语言的解释
  • 没问题,很高兴我能帮上忙
【解决方案2】:

这似乎可以做到:

package main

type toto struct { name string }

func transform (data ...interface{}) {
   t := data[0].(*toto)
   t.name = "tutu"
}

func main() {
   var titi toto
   transform(&titi)
   println(titi.name == "tutu")
}

【讨论】:

    【解决方案3】:

    听起来你想要一个指针。

    在您的示例中,您使用了interface{} 的数组,这有什么特别的原因吗?一般来说,你应该明确地使用 Go 中的类型,特别是因为你正在处理一个简单的结构。

    package main
    
    import (
        "log"
    )
    
    type toto struct {
        Name string
    }
    
    // to help with printing
    func (t *toto) String() string {
        return t.Name
    }
    
    // transform takes an array of pointers to the toto struct
    func transform(totos ...*toto) {
        log.Printf("totos before: %v", totos)
    
        // unsafe array access!
        totos[0].Name = "tutu"
    
        log.Printf("totos after: %v", totos)
    }
    
    func main() {
        // variables in Go are usually defined like this
        titi := toto{}
    
        transform(&titi)
    }
    

    【讨论】:

    • 可悲的是,我的问题是关于我为来自 lib 的测试而模拟的一个函数,我无法修改 function 的定义。但是感谢您对语言的解释。抱歉,我忘了准确指出这一点
    猜你喜欢
    • 1970-01-01
    • 2012-07-16
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多