我有一个日期时间的字段它有可能为空,我问 Go 该怎么办……

156 天前
 twig

祂说:「孩子,我们聊聊世界和平吧。」

最后是用 sql.NullTime 搞定的。我感到很惊奇。解决了别的语言不存在的问题。

但是我也很困惑,当初没有像 Rust 一样杜绝 nil ,那既然允许了别的值为 nil ,为什么 time.Time 不可以允许 nil 呢?

4651 次点击
所在节点    Go 编程语言
28 条回复
quqiu
156 天前
能啊,你用指针 *time.Time
twig
156 天前
@quqiu 啊哈,有可能行。我刚试的时候可能是油别的地方没搞对。明天再试试,
twig
156 天前
* 油 -> 有
southwolf
156 天前
没错啊 比较常见的做法就是 `*time.Time` 和 `sql.NullTime`
crackidz
156 天前
第一次用 null safety 语言吗...
bv
156 天前
就用 time.Time 有什么不妥吗?
通过 time.Time.IsZero() 方法来判断是否为空,大多时候外部过来的 null 值,在业务层并不严格区分传过来的时候是 nil 还是空(特殊情况例外),会统一认定为无意义的空值。如果输出的 JSON 格式不想看到 0001-01-01 这样,可以 go1.22 可以在 json tag 中加上 omitzero 。
iseki
156 天前
@crackidz 这个其实不是 null safety ,这个是值类型。
iseki
156 天前
因为 time.Time 是个值,不是引用,只有引用才可以是 null 。
*time.Time 是 pointer ,是引用,所有的 interface 都是引用。
bv
156 天前
就算用 *time.Time 或 sql.NullTime 也需要至少判断 if at != nil {} 或者 if at.Valid {} 来确定是否为 nil ,确定不为 nil ,也不代表 at 是一个有意义的时间。比如可能还是 at.IsZero()
iseki
156 天前
@bv 不是的,sql.NullTime 这个东西如果 Valid ,那么里面的 Time 就是有意义的。就算它 IsZero()=true 它也是有意义的,0001-01-01 是个有意义的时间点
bv
156 天前
@iseki #10 看我 #6 回复:大多数业务层会把 IsZero() 这种当作无意义的值忽略处理,如果你严格区分 null 和 零值,那确实需要指针或者 sql.NullTime 之类的
freestyle
156 天前
也可以在写 sql 的时候写 ifnull(field, date(0))
nodesolar
156 天前
用指针就行了
bv
156 天前
@iseki #10 我要表达的意思是,举个例子:
假设数据库中有个 vip 字段。0-不是 VIP 1-VIP 2-超级 VIP 。如果数据库中存在一个 vip 字段是 null ,但是 Go 中定义照样可以是 int ,因为 null 映射为 int 零值:0 ,null 和 0 都代表不是 VIP 。if vip != 0 { // 是尊贵的 VIP }如果 Go 设计成 *int ,就需要 if vip != nil && vip != 0 { // 是尊贵的 VIP} 这种判断了。

要不要区分 null value 和 zero value ,我认为大多情况下,设计好了是不需要区分的。但是不代表任何场景都不用区分。主要还得结合业务匹配最佳实践。

如果不为了追究最佳实践,其实 指针 和 sql.NullXXX 确实是通用的解决思路。
Huelse
156 天前
这种时候就体现语义严格的好处了,Rust 和 Scala 中的设计 Some 就是有 None 就是无,nil null pointer 是绝对不允许出现的,go 的话好像也有相关的 Option 库
niubiman
156 天前
你就说用 golang 写业务蛋疼不蛋疼吧
lvlongxiang199
156 天前
自己造一个 Optional<T>
hessian
156 天前
这个确实就很蛋疼,状态字段设计的时候都特意规避 0 值的使用,都是-1,1,2,3 这样。
neoblackcap
156 天前
@Huelse 其实就是要不要区分零值跟 NULL 值,只不过很多人想要的是 NULL 值,但是 golang 默认给了零值( golang 默认初始化所有值)
一开始就设计错了,导致后面积重难返
PTLin
156 天前
这也算是 go 里最经典的坑了
```
package main

import (
"encoding/json"
"fmt"
)

type User struct {
Name string
Age int
}

func main() {
text := `{"Name":"Bob"}`
var user User
err := json.Unmarshal([]byte(text), &user)
if err != nil {
panic(err)
}
fmt.Println(user)
}
```
直觉上应该报错,但是实际上解析成功了,但是 Age 是 0 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1127665

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX