golang,获取一条记录,数据库找不到记录,是返回特殊的 error 还是返回 nil , error 也返回 nill - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
void1900
V2EX    Go 编程语言

golang,获取一条记录,数据库找不到记录,是返回特殊的 error 还是返回 nil , error 也返回 nill

  •  
  •   void1900 2016-07-29 12:56:43 +08:00 2318 次点击
    这是一个创建于 3405 天前的主题,其中的信息可能已经有所发展或是发生改变。

    golang ,获取一条记录,数据库找不到记录,是返回 error 还是 返回对象 nil , error 也返回 nill

    比如用用户 ID 获取用户资料

    我是返回一个特殊的 error

    func (* UserModel) GetUser(uid uint) user User, err error { .... return user, ErrorNotFound } 

    还是返回 nil, nil

    func (* UserModel) GetUser(uid uint) user *User, err error { .... return nil, nil } 
    26 条回复    2016-08-01 17:04:16 +08:00
    ryanking8215
        1
    ryanking8215  
       2016-07-29 13:03:19 +08:00
    我选 nil, nil
    armandinho
        2
    armandinho  
       2016-07-29 13:11:44 +08:00
    通常是这么来的。

    func (* UserModel) GetUser(uid uint) user *User, err error {
    ....
    return nil, nil
    }

    func (* UserModel) MustGetUser(uid uint) user User, err error {
    ....
    return user, ErrorNotFound
    }
    void1900
        3
    void1900  
    OP
       2016-07-29 13:23:22 +08:00
    @ryanking8215 我也喜欢 nil,nil 这种 不过我刚开始用 golang ,所以比较不确定。。
    void1900
        4
    void1900  
    OP
       2016-07-29 13:23:53 +08:00
    @armandinho 好像也可以,不过一般不会定义两个功能几乎一样的方法吧。。
    leitwolf
        5
    leitwolf  
       2016-07-29 13:26:23 +08:00
    user=nil,err=errors.New("user not found")
    return;
    windy0925
        6
    windy0925  
       2016-07-29 13:31:26 +08:00
    error 应该返回 nil
    cloudzhou
        7
    cloudzhou  
       2016-07-29 13:32:44 +08:00
    这个问题在于 GetUser 没有找到用户,到底算不算一种 error ,就看你的定义了
    如果算 error ,一个比较麻烦的地方是,按照 go 的哲学,所有 error 都要明显的 check
    在检测错误的时候,你还需要排除 ErrorNotFound 这种情况,就比较繁琐
    zhujinliang
        8
    zhujinliang  
       2016-07-29 13:39:39 +08:00 via iPhone
    @armandinho
    Must 开头的函数应该不返回错误,有错误就 panic
    yanyuan2046
        9
    yanyuan2046  
       2016-07-29 13:39:45 +08:00
    我使用第二种,在实际项目中每个错误码都是特定的,查询错误是 ErrGetData ,数据不存在是 ErrDataNotExist ,以此类推。这个错误码不是字符串,而是一个对象
    type errorCode {
    ID string
    Namespace string
    Code int32
    Message string
    }
    huijiewei
        10
    huijiewei  
       2016-07-29 13:41:13 +08:00
    返回 nil ,不管啥语言,不要在内部乱抛出异常
    qwepoidjdj
        11
    qwepoidjdj  
       2016-07-29 15:18:44 +08:00
    这里看你对错误的定位
    我是这样想的(做的) 看这个方法具体面对的层级
    如果只是在数据库访问层级 返回 nil 肯定是比 err 合理 因为对于数据库本身来说 就是没有查到数据而不是产生了错误
    但是对于业务处理层来说 可能拿到了 nil 以后需要返回一个 err 来通告操作的人
    gamexg
        12
    gamexg  
       2016-07-29 15:22:45 +08:00
    nil,nil

    实现简单,不用费事的区分开数据库错误还是不存在。
    否则还需要定义不同的 error 来区分是数据库错误还是不存在。
    evilgod528
        13
    evilgod528  
       2016-07-29 18:45:05 +08:00
    mgo 是返回 ErrNotFound
    evilgod528
        14
    evilgod528  
       2016-07-29 18:47:47 +08:00
    如果返回 nil,nil 那么,每次得到的 user 还得检查是否 nil ,而如果你查到到的 error 为 nil 的话,那么 user 可能有值
    evilgod528
        15
    evilgod528  
       2016-07-29 18:48:33 +08:00
    @evilgod528 user 肯定有值,打快了,敲错字了
    magicdawn
        16
    magicdawn  
       2016-07-29 19:14:53 +08:00
    @armandinho

    Must 一般是 panic 吧
    janxin
        17
    janxin  
       2016-07-29 19:30:42 +08:00
    return user, ErrorNotFound
    janxin
        18
    janxin  
       2016-07-29 19:34:53 +08:00
    另外其实 golang 中的 error 是个 interface ,所以利用 @yanyuan2046 提到的错误扩展方式其实非常方便
    orvice
        19
    orvice  
       2016-07-29 20:28:34 +08:00
    nil nil
    zhx1991
        20
    zhx1991  
       2016-07-29 20:50:08 +08:00
    看业务.
    raincious
        21
    raincious  
       2016-07-29 20:57:14 +08:00
    我觉得这种问题没什么好纠结的。

    返回 error 是考虑到后面的过程能截获这个错误然后进行处理,而标记成 Must 的函数则是这个函数出现错误之后,程序完全没必要继续运行下去了。

    GetUser 找不到记录的话,应该返回 nil 指针附带一个错误,指明发生了什么(比如是什么导致无法获取用户的,是没有记录,服务器挂了,服务器拒绝处理还是其他什么)。你可以参考 Google 在自己 AppEngine 系统上的实现:
    https://cloud.google.com/appengine/docs/go/datastore/reference#variables

    当 Datastore 尝试获取一个不存在的对象时,会返回 ErrNoSuchEntity 这个 error 。
    aphasia
        22
    aphasia  
       2016-07-29 23:51:59 +08:00
    这个跟你业务场景,以及设计有关,建议用 2 ,否则给自己留坑,不信你试试
    如果用 2 的话,可以在调用函数时区分出到底是同 db 交互失败,还是交互成功但却查询为空,可以在调用时区别地对待这两种结果……
    不信你试试 1 ……
    julor
        23
    julor  
       2016-07-30 07:44:11 +08:00 via Android
    查询结果为空,返回 not found ,与数据交互出现的无知,直接返回上级错误
    janxin
        24
    janxin  
       2016-07-30 09:50:57 +08:00 via iPhone
    @aphasia 这两种在区分功能上并没有区别吧
    darasion
        25
    darasion  
       2016-08-01 08:27:04 +08:00
    我认为,
    如果系统其他部分可能会报错,比如数据库可能连接不上, cache 可能挂了什么的,那返回值就需要第二个 error 参数
    如果系统其他部分根本不可能报错,就是一个普普通通的数据类型,没有跟其他系统交互,那么就没有第二个 error 返回,找不到就直接一个 nil 就好了, nil 本身就是找不到的意思,互为充分必要条件。
    至于返回是否是 nil 的检查,我想应该适当的抛给上层逻辑来处理。具体情况具体分析。
    leedstyh
        26
    leedstyh  
       2016-08-01 17:04:16 +08:00
    我也纠结过,现在的做法是:

    定义一个`UserNotFound`的空 User struct ,如果查询没有结果,就返回`UserNotFound, nil`。

    给 User 这个 struct 定义一个`IsNil()`的方法,一般来说用户名不能为空,所以就根据用户名来判断这个 user 是不是存在

    handler 里,如果`user.IsNil()`结果是 true ,就返回 404
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4494 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 04:03 PVG 12:03 LAX 20:03 JFK 23:03
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86