Go圣经-学习笔记之函数和错误处理

上一篇 Go圣经-学习笔记之复合类型(三)

下一篇 Go圣经-学习笔记之函数值(二)

函数声明

下面给出函数的四种声明方法:

func add(x int, y int) int { return x+y }

func add(x, y int) (z int) { z = x + y ; return z }

func first(x int, _ int) int { return x }
func second(int, int) int { return 0 }

存在必有其存在的价值,那后两者存在的应用场景在哪里呢?我表示持续懵逼中......, 我写了这个场景,看可以不?

type Interface interface {
    Add(int, int) int
    Sub(int, int) int
    Mul(int, int) int
}

type Idem struct{}

func (i Idem) Add(x int, y int) int {
    return x + y
}

func (i Idem) Sub(int, int) int {
    return 0
}

func (i Idem) Mul(x int, _ int) int {
    return x * 2
}

Go语言有一个非常核心的特性:组合,上一篇文章也提及了。如果一个对象是由多个接口、匿名方法集或者对象的自身行为集构成。如果这个对象觉得有些行为是它忽略或者不想要的,这时候就可以采用上述的后两者方法去声明方法,但是这个是方法的声明,不是函数的声明。能想到的就这么多,其他我就不知道了。

函数递归

对于递归的运用,我们通过一个url,获取网页数据流,然后通过html标准库解析数据,获取各个标签元素节点。

说明: 运用html标准库解析完数据流,得到的是一个多叉树的元素节点,包括:文档节点、文本节点、元素标签节点、评论节点、注脚节点和错误节点。由这六个节点构成一颗多叉树

DEMO实例代码片段

**********************************************************************
// html标准库相关说明:
type Node struct {
    Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node

    Type      NodeType
    DataAtom  atom.Atom
    Data      string
    Namespace string
    Attr      []Attribute
}
func Parse(r io.Reader) (*Node, error)
**********************************************************************


var (
    purl = flag.String("url", "https://www.golang.org", "website url")
)

func main() {
    var (
        resp *http.Response
        err  error
        node *html.Node
    )
    flag.Parse()
    if resp, err = http.Get(*purl); err != nil {
        log.Fatal(err.Error())
        return
    }
    node, err = html.Parse(resp.Body)
    if err != nil {
        log.Fatal(err.Error())
        return
    }
    resp.Body.Close()
    // 我们采用多叉树的深度遍历算法,并打印所有节点元素url。
    for _, link := range visit(nil, node) {
        fmt.Println(link)
    }
}

func visit(links []string, node *html.Node) []string {
    if node.Type == html.ElementNode && node.Data == "a" {
        for _, attr := range node.Attr {
            links = append(links, attr.Val)
        }
    }
    for child := node.FirstChild; child != nil; child = child.NextSibling {
        links = visit(links, child)
    }
    return links
}

递归元素的存储是由stack数据结构存储的。C++等主流语言的stack栈是由固定大小的,如果过多递归会导致栈内存溢出。但是Go语言的栈是动态分配的,按需分配,一般不需要考虑stack溢出安全问题,但是还是要多多尽量节约内存

大家吐槽比较多的函数返回error

在Go语言标准库使用过程中,几乎每个函数或者方法返回参数的最后一个类型都是error,类似:

func xx(sss type, ...) (xxx type, ..., err error)

比如,我们经常这样做:

***************************************************
// ioutil标准库:
func ReadAll(r io.Reader) (data []byte, err error)
***************************************************

if bts, err := ioutil.ReadAll(r); err !=nil{
    err = errors.Wrap(err, "xxxxx")
    return
}

大家觉得如果调用的每个方法或者函数,都要处理error,大家比较崩溃。Go语言官方是这样解释返回error的设计的:

由于对于某个应该在控制流程中处理的错误而言,将这个错误以异常的形式抛出会混乱对错误的描述,这通常会导致一些糟糕的后果。当某个程序错误被当作异常处理后,这个错误会将堆栈信息返回给用户,这些信息复杂且无用,无法帮助定位错误

所以Go语言想通过控制流if,return语句处理异常,这使得编程人员能更多的关注错误处理。

在这里给大家提供用于error追踪的包,很好用。github地址:errors

// 我一般这样使用
// 业务逻辑层
func AddSaleOrder(so *SaleOrder, o *orm.Ormer) (retCode int, err error) {
    if _, err = (*o).Insert(so); err != nil {
        err = errors.Wrap(err, "AddSaleOrder")
        retCode = consts.DB__INSERT_ERROR
        return
    }
    return
}

// 控制层
func (s *SaleOrderController) AddSaleOrder() {
    var so *models.SaleOrder
    ......
    if retCode, err = models.AddSaleOrder(so, nil); err !=nil {
        Logger.Error(err.Error()) // 这里的err.Error() 会返回整个的error跟踪调用链
        s.Data["json"] = map[string]interface{}{
            "err_code": retCode,
            "err_msg": errors.Cause(err).Error(), // 这个返回err调用链中产生错误最初始的形态。
        }
        s.ServeJSON()
        return
    }
    s.Data["json"] = map[string]interface{}{
        "err_code": 0,
        "err_msg": "",
    }
    s.ServeJSON()
    return
}

这样做的好处是:我们服务端既可以记录错误产生的整个调用链跟踪,同时,前端也返回了关键错误。屏蔽了不想要用户看到的信息。

转载于:https://my.oschina.net/u/3287304/blog/1555654

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值