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

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


shook 于 2016-10-24 14:22 修改
22 回复
stevewang
#1 stevewang • 2016-10-24 15:29

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

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呢?

shook
#2 shook • 2016-10-24 15:50

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

stevewang
#3 stevewang • 2016-10-24 16:02

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

shook
#4 shook • 2016-10-24 16:46

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

stevewang
#5 stevewang • 2016-10-24 16:56

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

shook
#6 shook • 2016-10-24 17:14

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

shook
#7 shook • 2016-10-25 10:13

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

stevewang
#8 stevewang • 2016-10-25 10:34

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

shook
#9 shook • 2016-10-25 11:00

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

ssqq
#10 ssqq • 2016-10-25 11:17

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

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

stevewang
#11 stevewang • 2016-10-25 11:17

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

shook
#12 shook • 2016-10-25 11:35

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

shook
#13 shook • 2016-10-25 11:42

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

huangxianghan
#14 huangxianghan • 2016-10-26 10:29

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

type IString interface{}

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

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

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

huangxianghan
#15 huangxianghan • 2016-10-26 10:50
额,那个写错了。这个可以
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}
shook
#16 shook • 2016-10-27 09:54

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

imjj
#17 imjj • 2016-10-27 17:18

加个函数也许方便一些

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)
}
shook
#18 shook • 2016-10-28 19:31

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

shook
#19 shook • 2016-10-28 19:57

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

joeonly
#20 joeonly • 2016-10-30 02:49

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

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

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

jong1985
#21 jong1985 • 2016-10-31 10:20

楼主,我自己也写了一个orm,大家交流交流
https://github.com/shengzhi/shorm

shook
#22 shook • 2016-11-03 20:47

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

需要 登录 后方可回复, 如果你还没有账号你可以 注册 一个帐号。