我们可以用这种方式在golang中创建结构。下面的示例:两者之间有什么区别?
// Usual way type Employee struct { firstName string `json:"name"` salary int `json:"salary"` fullTime bool `json:"fullTime"` projects []Project `json:"projects"` } // Un-usal way with pointers type Employee struct { firstName *string `json:"name"` salary *int `json:"salary"` fullTime *bool `json:"fullTime"` projects *[]Project `json:"projects"` }
是否有诸如内存之类的取舍?
更新:
假设以下功能:
// this function consumes MORE memory func printEmployeeWithoutPointer(employee Employee) { // print here } // this function consumes LESS memory func printEmployeeWithPointer(employee *Employee) { // print here }
是的,有很多事情要考虑。首先:让我们从指针示例中明显的语法错误开始:
type Employee struct { FirstName *string `json:"name"` Salary *int `json:"salary"` FullTime *bool `json:"fullTime"` }
因此,我已将星号移至该类型,并已将字段强制化。该encoding/json包使用反射来设置字段的值,因此需要将其导出。
encoding/json
看到您正在使用json标签时,让我们从简单的事情开始:
type Foo struct { Bar string `json:"bar"` Foo *string `json:"foo,omitempty"` }
当我解组没有bar价值的消息时,该Bar字段将只是一个空字符串。这使得很难确定是否发送了该字段。尤其是在处理整数时:如何区分未发送的字段与发送的值为0的字段之间的区别? 使用指针字段,并指定omitempty可以执行此操作。如果未在JSON数据中指定该字段,则结构中的字段将为nil,如果未指定:它将指向值为0的整数。
bar
Bar
omitempty
nil
当然,必须检查指针是否为n可能很乏味,这会使代码更容易出错,因此,只有在有实际原因要区分未设置的字段时,才需要这样做。零值。
让我们继续探讨指针所固有的风险。假设您的Employee结构体具有指针字段,并且类型EmployeeV相同,但具有值字段,请考虑以下功能:
Employee
EmployeeV
func (e Employee) SetName(name string) { if e.Firstname == nil { e.Firstname = &name return } *e.Firstname = name }
现在,此功能仅在一半时间内起作用。您正在呼叫SetName价值接收者。如果Firstname为nil,则将指针设置在原始变量的副本上,并且变量将不会反映您在函数中所做的更改。如果Firstname 被 设置,但是,副本将指向相同的字符串作为你的原始变量,而指针指向的值 将 得到更新。那很糟。
SetName
Firstname
在上实现相同的功能EmployeeV:
func (e EmployeeV) SetName(name string) { e.Firstname = name }
它根本不会 永远 工作。您将始终更新一个副本,并且所做的更改不会影响调用该SetName函数的变量。因此,执行类似操作的惯用方式是:
type Employee struct { Firstname string // other fields } func (e *Employee) SetName(name string) { e.Firstname = name }
因此,我们正在更改使用指针接收器的 方法 。
与往常一样:如果您使用指针,则实际上是在允许代码操纵直接指向的内存。鉴于golang是一种众所周知的促进并发的语言,访问相同的内存意味着您有创建数据争用的风险:
func main() { n := "name" e := Employee{ Firstname: &n, } go func() { *e.Firstname = "foo" }() race(e) } func race(e Employee) { go race(e) go func() { *e.Firstname = "in routine" }() *e.Firstname = fmt.Sprintf("%d", time.Now().UnixNano()) }
Firstname可通过许多不同的例程访问此字段。最终的价值是什么?你还不知道吗 golang竞赛检测器很可能将此代码标记为潜在的数据竞赛。
就内存使用而言:int或bool之类的单个字段实际上并不是您应该担心的事情。如果您要传递一个相当大的结构,并且知道它是安全的,那么传递一个指向所述结构的指针可能是一个好主意。再说一次,通过指针访问值而不是直接访问值不是免费的:间接添加会增加少量开销。