Golang的指针赋值有没有更方便的方法?

type Test struct {
    ID   int64
    Name *string
    PWD  string
    Time time.Time
}

func main() {
    t := new(Test)
    name := "zhangsan"
    t.Name = &name
    fmt.Println(*t.Name)
}

这种指针赋值需要写两条语句,写起来麻烦,而且还要多定义一个变量,有没有更方便更好的写法呢?
我试了*t.Name="zhangsan"不行,因为这种写法好像不是赋值,而是去修改地址的值,由于初始没有地址,所以报错了:

panic: runtime error: invalid memory address or nil pointer dereference

大家有没有更好的赋值方式?

共 24 个回复


stevewang

需要两条语句,但是不需要多定义一个变量:

type Test struct {
    ID   int64
    Name *string
    PWD  string
    Time time.Time
}

func main() {
    t := new(Test)
    t.Name = new(string)
    *t.Name = "zhangsan"
    fmt.Println(*t.Name)
}

另外,一般很少需要用到*string类型。你为什么不直接使用string呢?

# 0

shook

@stevewang 还不是为了判断结构里面哪个字段没有数据,golang都有默认值,要想判断哪个字段为空我知道的方法就是判断指针为nil,不知道你有没有更好的方法。

# 1

stevewang

@shook database/sql有个NullString类型,也许你可以直接使用。

# 2

shook

@stevewang 应该不是这个,因为还没到数据库的层面呢,还只是判断结构体的数据。

# 3

stevewang

我的意思是,你可以直接使用NullString类型根据Valid的值来处理nil的情况,不是一定要用数据库。或者把那个类型的定义复制到自己的包里 也可以。

# 4

shook

哦,这样哈,那我晚上试试。

# 5

shook

@stevewang 看了一下,没搞懂怎么用…isValid()出来的值都是true,没法用Valid的指来处理nil…

# 6

stevewang

Valid值是你自己设置的,如果字符串是nil,Valid就设置为false,否则设置为true。

# 7

shook

这也是一个办法,只是处理起来也不是很方便,现在有两个办法了,我想想做哪一种,谢谢哈!

# 8

ssqq

字符串不需要引用,第一因为字符串不能修改,第二因为系统会自动优化传递字符串参数,和指针差不多。参看 <<Go语言程序设计>> 109 页。

“go 语言编译器会将传递过程进行安全的优化,因此无论传递的字符串长度多少,实际传递的数据量都会非常小,每个字符串的代码在64位机器上大概是16个字节,在32位的机器上大概是8字节。但如果修改一个字符串,就必须创建新的字符串。代码可能非常大。”

# 9

stevewang

*string相当于多了一次指针访问,性能上会稍微差一些;另外如果数量很多的话,GC压力也会增加,因为要额外在堆上创建很多的string对象。

# 10

shook

@ssqq 嗯,我只是拿字符串来举例,其实也不是为了要去改它,只是想通过它来判断Struct的某个字段是否有值而已,没其他办法的情况下才想这么干的,就是感觉Golang判断值为空好麻烦,都有默认值!

# 11

shook

@stevewang 用了其他的ORM总感觉被强加了很多东西,就想自己写个适合自己的ORM,其他的地方都勉强做好了,但是在update这儿卡了几天了,一直没个好办法来判断哪些Struct字段有值(我设想的是增删改查都传Struct)。

# 12

huangxianghan

字段类型 设为interface{} 就可以放nil了,如果怕取值麻烦可以学西java的包装类。

type IString interface{}

func (self *IString) String() string{
    return self.(string)
}

func (self *IString) IsNil() bool {
    return self == nil
}

每个基本类型都自己定义一个包装类型。

# 13

huangxianghan

额,那个写错了。这个可以
type IString struct {
    Value string
}

func (self *IString) String() string {
    return self.Value
}

type Test struct {
    ID   int64
    Name *IString
    PWD  *IString
    Time time.Time
}

func main() {

    t := new(Test)
    t.Name = &IString{"aaaaa"}
    fmt.Println(t)
}

输出 &{0 aaaaa <nil> 0001-01-01 00:00:00 +0000 UTC}
# 14

shook

@huangxianghan 非常感谢您的回复,你的解决方法又给了我一个思路,你的方式和@stevewang的方式差不多,都是自定义类型和方法,看来只有从这个方面找出路了。

# 15

imjj

加个函数也许方便一些

package main

import "fmt"
import "time"

type Test struct {
    ID   int64
    Name *string
    PWD  string
    Time time.Time
 }
func StringPtr(s string) *string {
   return &s
}
func main() {
    t := new(Test)
    t.Name = StringPtr("hello")
    fmt.Println(*t.Name)
}
# 16

shook

@imjj 厉害,你的回答简直是完美解决了我开始提出的问题!这么简单我居然都想不到…
虽然我现在可能会用其他办法做这个事情,但是你的答案绝对是我开始最想要的答案!

# 17

shook

不过写了ORM才觉得Golang有的地方真的挺烦的,一个数据类型还不能从头用到底,数据开始定义为String,不能直接判断空值是第一个坑,从数据库读出来的数据得换成NullString或者RawByte之类的,不然读到空值会Panic,这是第二个坑,最后还得换回原类型,换来换去的,一不注意就出问题了。
感觉Golang对新手真的不怎么友好,经常遇到坑,当然我目前也只认真学过Golang,其他语言也不会,不知道其他语言是不是也这样。以前只是会SQL写写报表啥的。

# 18

joeonly

问题是解决了,但为取字符串地址加一个方法总有点别扭吧?我更愿意加个工厂方法。

另外大家为什么那么喜欢 new 一个东西,是为了直观吗?如果不是,个人感觉如果不加工厂方法,是不是用
&Test{Name: StringPtr("zhangsan")}
更方便?

个人觉得 GO 的上面这种写法比 JAVA 构造函数的方式还方便,想初始化哪个字段就初始化哪个,不用搞那么多重载。

# 19

shook

@jong1985 我只是试着做了一下,连成品都没有,去拜读拜读你的源码。

# 21

rocket

楼主没把教程看明白吧!

未赋值的string有默认的零值:""

type Test struct {
    ID   int64
    Name string
    PWD  string
    Time time.Time
}

func main() {
    t := &Test{ID:1,Name:"zhangsan",PWD:"passwd",Time:Time.Now()}
    fmt.Println(t.Name)
}
# 23