如何在Go中安全地检测结构体的零值?

huangapple go评论147阅读模式
英文:

How to detect a zeroed value of struct safely in Go?

问题

我尝试为MySQL编写了一个简单的ORM,遇到了一个问题:如果一个定义的结构体缺少某些字段:

type User struct {
    ID        int64
    Username  string
    Password  string
    Email     string
    Comment   string
}

var u = User{Username: "user_0001", Password: "password"}

User的某些字段没有给定值,那么它们的值将是零值,比如字符串"",布尔值false,整数0等。

因此,我使用reflect来获取字段名和值,生成一个插入行的SQL语句。

INSERT INTO User (Id, Username, Password, Email, Comment) Values (0, "user_0001", "password", , ,)

你可以看到字符串中有一些零值,如果我检测到空字符串""并跳过它们,可能会跳过正常的值。

英文:

I tried to written an simple ORM for MySQL, I got a problem, if a defined struct missing some field:

type User struct {
    ID        int64
    Username  string
    Password  string
    Email     string
    Comment   string
}

var u = User{Username: "user_0001", Password: "password"}

Some fields of User didn't given a value, then it's value will be a zeroed value, such as string "", bool false, integer 0 and so on.

So I am using reflect to get field name and value, generate a sql to insert row.

INSERT INTO User (Id, Username, Password, Email, Comment) Values (0, "user_0001", "password", , ,)

You can see there has some zeroed value of string, if I detect empty string "" and skip them, I may skip normal value.

答案1

得分: 2

处理可能为NULL的数据库列,可以使用指针,就像Friedrich Große建议的那样,或者使用db包中的Null...变体,例如sql.NullString

要使用Null变体,结构体应该是这样的:

type User struct {
    ID        int64
    Username  string
    Password  string
    Email     sql.NullString
    Comment   sql.NullString
}

然后,您可以通过检查NullString.Valid来检测值是否已设置。使用NullString的缺点是,在打印或编组它们时,您必须添加特殊情况,因为它们不实现Stringer接口,也不实现MarshalJSONUnmarshalJSON。您还必须记住在设置字符串的值时手动设置NullString.Valid

例如,像这样的测试:

func TestNullString(t *testing.T) {
    s := sql.NullString{}
    t.Log(s)

    s.String = "Test"
    s.Valid = true
    t.Log(s)
}

输出:

null_string_test.go:21: { false}
null_string_test.go:25: {Test true}

要打印NullString,您必须这样做:

func TestNullString(t *testing.T) {
    s := sql.NullString{}
    t.Log(s.String)

    s.String = "Test"
    s.Value = true
    t.Log(s.String)
}

输出:

null_string_test.go:21: 
null_string_test.go:25: Test
英文:

To handle database columns that may be NULL, you can either use pointers as Friedrich Große suggests, or use the Null...-variants found in the db package, for instance sql.NullString.

To use the Null-variants, the struct would be

type User struct {
    ID        int64
    Username  string
    Password  string
    Email     sql.NullString
    Comment   sql.NullString
}

You can then detect of a value is set by checking NullString.Valid. The downside of using NullString is that you have to add special cases when printing or marshaling them, as they don't implement the Stringer interface, nor MarshalJSON or UnmarshalJSON. You also have to remember to set NullString.Valid manually when setting the value of the string.

For instance, a test like

func TestNullString(t *testing.T) {
    s := sql.NullString{}
    t.Log(s)

    s.String = "Test"
    s.Valid = true
    t.Log(s)
}

Prints

null_string_test.go:21: { false}
null_string_test.go:25: {Test true}

To print a NullString you have to instead do

func TestNullString(t *testing.T) {
    s := sql.NullString{}
    t.Log(s.String)

    s.String = "Test"
    s.Value = true
    t.Log(s.String)
}

Which prints:

null_string_test.go:21: 
null_string_test.go:25: Test

答案2

得分: 1

你可以将一个空字符串插入到你的表列中。如果真的很重要要区分零值和完全缺少值之间的区别,你需要使用指针。

type User struct {
    ID        int64
    Username  *string
}

这样会将零值更改为nil,这样你就可以将其与""区分开来。
缺点是这使得这种类型不太容易使用(在实践中经常会忘记进行nil检查),而且你必须解引用该字段才能获得实际的字符串。

在你的具体示例中,我不明白为什么你需要担心空值。你不能只将""插入到数据库中,并在代码中强制执行验证(非空性),而不使用数据库约束吗?

英文:

You can always just insert an empty string into your table column. If it is truly important to know the difference between the zero value or complete absence of a value you would need to use pointers.

type User struct {
    ID        int64
    Username  *string
}

This changes the zero value to be nil so you can distinguish that from "".
The downside is that this makes this type less easy to use (nil checks are often forgotten in practice) and you have to dereference that field to get the actual string.

In your specific example I don't see why you need to worry about the empty value at all. Can't you just insert "" into the database and enforce validation (non-emptiness) in your code instead of using database constraints?

huangapple
  • 本文由 发表于 2016年4月24日 15:33:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/36820470.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定