哩哩啦啦 發表於 2019-9-23 11:34:00

Go语言(golang)新发布的1.13中的Error Wrapping深度分析

<p id="activity-name" class="rich_media_title">&nbsp;</p>
<div id="js_content" class="rich_media_content ">
<p>Go 1.13发布的功能还有一个值得深入研究的,就是对Error的增强,也是今天我们要分析的 Error Wrapping.</p>
<h2>背景</h2>
<p>做Go语言开发的,肯定经常用<code>error</code>,但是我们也知道<code>error</code>非常弱,只能自带一串文本其他什么都做不了,比如给已经存在的<code>error</code>增加一些附加文本,增加堆栈信息等都做不了。如果我们想给<code>error</code>增加一些附加文本怎么做呢?有两种办法:</p>
<p>第一种:</p>
<pre><code>newErr:=fmt.Errorf(<span>"数据上传问题:&nbsp;%v",&nbsp;err)<br></span></code></pre>
<p>通过<code>fmt.Errorf</code>函数,基于已经存在的<code>err</code>再生成一个新的<code>newErr</code>,然后附加上我们想添加的文本信息。这种办法比较方便,但是问题也很明显,我们丢失了原来的<code>err</code>,因为它已经被我们的<code>fmt.Errorf</code>函数转成一个新的字符串了。</p>
<p>第二种:</p>
<pre><code><span><span>func&nbsp;<span>main<span>()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;newErr&nbsp;:=&nbsp;MyError{err,&nbsp;<span>"数据上传问题"}<br>}<br><br><span>type&nbsp;MyError&nbsp;<span>struct&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;error<br>&nbsp;&nbsp;&nbsp;&nbsp;msg&nbsp;<span>string<br>}<br><br><span><span>func&nbsp;<span>(e&nbsp;*MyError)&nbsp;<span>Error<span>()&nbsp;<span>string&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;e.err.Error()&nbsp;+&nbsp;e.msg<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>这种方式就是我们自定义自己的<code>struct</code>,添加用于存储我们需要额外信息的字段,我这里是<code>err</code>和<code>msg</code>,分别存储原始<code>err</code>和新附加的出错信息。然后这个<code>MyError</code>还会实现<code>error</code>接口,表示他是一个<code>error</code>,这样我们就可以自由的使用了。</p>
<p>这种方式有点很明显,大家可以看到了。缺点呢,就是我们要自定义很多<code>struct</code>,而且我们自己定义的,和第三方的可能还不太一样,无法统一和兼容。基于这个背景,Golang 1.13 为我们提供了Error Wrapping,翻译过来我更愿意叫Error嵌套。</p>
<h2>如何生成一个Wrapping Error</h2>
<p>Error Wrapping,顾名思义,就是为我们提供了,可以一个<code>error</code>嵌套另一个<code>error</code>功能,好处就是我们可以根据嵌套的<code>error</code>序列,生成一个<code>error</code>错误跟踪链,也可以理解为错误堆栈信息,这样可以便于我们跟踪调试,哪些错误引起了什么问题,根本的问题原因在哪里。</p>
<p>因为<code>error</code>可以嵌套,所以每次嵌套的时候,我们都可以提供新的错误信息,并且保留原来的<code>error</code>。现在我们看下如何生成一个嵌套的<code>error</code>。</p>
<pre><code>e&nbsp;:=&nbsp;errors.New(<span>"原始错误e")<br>w&nbsp;:=&nbsp;fmt.Errorf(<span>"Wrap了一个错误%w",&nbsp;e)<br></span></span></code></pre>
<p>Golang并没有提供什么<code>Wrap</code>函数,而是扩展了<code>fmt.Errorf</code>函数,加了一个<code>%w</code>来生成一个可以Wrapping Error,通过这种方式,我们可以创建一个个以Wrapping Error。</p>
<h2>Wrapping Error原理</h2>
<p>按照这种不丢失原<code>error</code>的思路,那么Wrapping Error的实现原理应该类似我们上面的自定义<code>error</code>.我们看下<code>fmt.Errorf</code>函数的源代码验证下我们的猜测是否正确。</p>
<pre><code><span><span>func&nbsp;<span>Errorf<span>(format&nbsp;<span>string,&nbsp;a&nbsp;...<span>interface{})&nbsp;<span>error&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//省略无关代码<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>var&nbsp;err&nbsp;error<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;p.wrappedErr&nbsp;==&nbsp;<span>nil&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;errors.New(s)<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span>else&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;&amp;wrapError{s,&nbsp;p.wrappedErr}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;p.free()<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;err<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>这里的关键核心代码就是<code>p.wrappedErr</code>的判断,这个值是否存在,决定是否要生成一个wrapping error。这个值是怎么来的呢?就是根据我们设置的<code>%w</code>解析出来的。</p>
<p>有了这个值之后,就生成了一个<code>&amp;wrapError{s, p.wrappedErr}</code>返回了,这里有个结构体<code>wrapError</code></p>
<pre><code><span>type&nbsp;wrapError&nbsp;<span>struct&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;msg&nbsp;<span>string<br>&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;error<br>}<br><br><span><span>func&nbsp;<span>(e&nbsp;*wrapError)&nbsp;<span>Error<span>()&nbsp;<span>string&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;e.msg<br>}<br><br><span><span>func&nbsp;<span>(e&nbsp;*wrapError)&nbsp;<span>Unwrap<span>()&nbsp;<span>error&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;e.err<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>如上所示,和我们想的一样。实现了<code>Error</code>方法说明它是一个<code>error</code>。<code>Unwrap</code>方法是一个特别的方法,所有的wrapping error 都会有这么一个方法,用于获得被嵌套的error。</p>
<h2>Unwrap 函数</h2>
<p>Golang 1.13引入了wrapping error后,同时为<code>errors</code>包添加了3个工具函数,他们分别是<code>Unwrap</code>、<code>Is</code>和<code>As</code>,先来聊聊<code>Unwrap</code>。</p>
<p>顾名思义,它的功能就是为了获得被嵌套的error。</p>
<pre><code><span><span>func&nbsp;<span>main<span>()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;:=&nbsp;errors.New(<span>"原始错误e")<br>&nbsp;&nbsp;&nbsp;&nbsp;w&nbsp;:=&nbsp;fmt.Errorf(<span>"Wrap了一个错误%w",&nbsp;e)<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println(errors.Unwrap(w))<br>}<br></span></span></span></span></span></span></code></pre>
<p>以上这个例子,通过<code>errors.Unwrap(w)</code>后,返回的其实是个<code>e</code>,也就是被嵌套的那个error。<br>这里需要注意的是,嵌套可以有很多层,我们调用一次<code>errors.Unwrap</code>函数只能返回最外面的一层<code>error</code>,如果想获取更里面的,需要调用多次<code>errors.Unwrap</code>函数。最终如果一个<code>error</code>不是warpping error,那么返回的是<code>nil</code>。</p>
<pre><code><span><span>func&nbsp;<span>Unwrap<span>(err&nbsp;error)&nbsp;<span>error&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//先判断是否是wrapping&nbsp;error<br>&nbsp;&nbsp;&nbsp;&nbsp;u,&nbsp;ok&nbsp;:=&nbsp;err.(<span>interface&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Unwrap()&nbsp;error<br>&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//如果不是,返回nil<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;!ok&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;<span>nil<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//否则则调用该error的Unwrap方法返回被嵌套的error<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;u.Unwrap()<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>看看该函数的的源代码吧,这样就会理解的更深入一些,我加了一些注释。</p>
<h2>Is 函数</h2>
<p>在Go 1.13之前没有wrapping error的时候,我们要判断error是不是同一个error可以使用如下办法:</p>
<pre><code><span>if&nbsp;err&nbsp;==&nbsp;os.ErrExist<br></span></code></pre>
<p>这样我们就可以通过判断来做一些事情。但是现在有了wrapping error后这样办法就不完美的,因为你根本不知道返回的这个<code>err</code>是不是一个嵌套的error,嵌套了几层。所以基于这种情况,Golang为我们提供了<code>errors.Is</code>函数。</p>
<pre><code><span><span>func&nbsp;<span>Is<span>(err,&nbsp;target&nbsp;error)&nbsp;<span>bool<br></span></span></span></span></span></code></pre>
<ol class="list-paddingleft-2">
<li>
<p>如果<code>err</code>和<code>target</code>是同一个,那么返回<code>true</code></p>
</li>
<li>
<p>如果<code>err</code>&nbsp;是一个wrap error,<code>target</code>也包含在这个嵌套error链中的话,那么也返回<code>true</code>。</p>
</li>
</ol>
<p>很简单的一个函数,要么咱俩相等,要么<code>err</code>包含<code>target</code>,这两种情况都返回<code>true</code>,其余返回<code>false</code>。</p>
<pre><code><span><span>func&nbsp;<span>Is<span>(err,&nbsp;target&nbsp;error)&nbsp;<span>bool&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;target&nbsp;==&nbsp;<span>nil&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;err&nbsp;==&nbsp;target<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;isComparable&nbsp;:=&nbsp;reflectlite.TypeOf(target).Comparable()<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//for循环,把err一层层剥开,一个个比较,找到就返回true<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>for&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;isComparable&nbsp;&amp;&amp;&nbsp;err&nbsp;==&nbsp;target&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;<span>true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>//这里意味着你可以自定义error的Is方法,实现自己的比较代码<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;x,&nbsp;ok&nbsp;:=&nbsp;err.(<span>interface{&nbsp;Is(error)&nbsp;<span>bool&nbsp;});&nbsp;ok&nbsp;&amp;&amp;&nbsp;x.Is(target)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;<span>true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>//剥开一层,返回被嵌套的err<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;err&nbsp;=&nbsp;Unwrap(err);&nbsp;err&nbsp;==&nbsp;<span>nil&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;<span>false<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p><code>Is</code>函数源代码如上,其实就是一层层反嵌套,剥开然后一个个的和<code>target</code>比较,相等就返回true。</p>
<h2>As 函数</h2>
<p>在Go 1.13之前没有wrapping error的时候,我们要把error转为另外一个error,一般都是使用type assertion 或者 type switch,其实也就是类型断言。</p>
<pre><code><span>if&nbsp;perr,&nbsp;ok&nbsp;:=&nbsp;err.(*os.PathError);&nbsp;ok&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println(perr.Path)<br>}<br></span></code></pre>
<p>比如例子中的这种方式,但是现在给你返回的err可能是已经被嵌套了,甚至好几层了,这种方式就不能用了,所以Golang为我们在<code>errors</code>包里提供了<code>As</code>函数,现在我们把上面的例子,用<code>As</code>函数实现一下。</p>
<pre><code><span>var&nbsp;perr&nbsp;*os.PathError<br><span>if&nbsp;errors.As(err,&nbsp;&amp;perr)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println(perr.Path)<br>}<br></span></span></code></pre>
<p>这样就可以了,就可以完全实现类型断言的功能,而且还更强大,因为它可以处理wrapping error。</p>
<pre><code><span><span>func&nbsp;<span>As<span>(err&nbsp;error,&nbsp;target&nbsp;<span>interface{})&nbsp;<span>bool<br></span></span></span></span></span></span></code></pre>
<p>从功能上来看,<code>As</code>所做的就是遍历err嵌套链,从里面找到类型符合的error,然后把这个error赋予target,这样我们就可以使用转换后的target了,这里有值得赋予,所以target必须是一个指针。</p>
<pre><code><span><span>func&nbsp;<span>As<span>(err&nbsp;error,&nbsp;target&nbsp;<span>interface{})&nbsp;<span>bool&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//一些判断,保证target,这里是不能为nil<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;target&nbsp;==&nbsp;<span>nil&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>panic(<span>"errors:&nbsp;target&nbsp;cannot&nbsp;be&nbsp;nil")<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;val&nbsp;:=&nbsp;reflectlite.ValueOf(target)<br>&nbsp;&nbsp;&nbsp;&nbsp;typ&nbsp;:=&nbsp;val.Type()<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//这里确保target必须是一个非nil指针<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;typ.Kind()&nbsp;!=&nbsp;reflectlite.Ptr&nbsp;||&nbsp;val.IsNil()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>panic(<span>"errors:&nbsp;target&nbsp;must&nbsp;be&nbsp;a&nbsp;non-nil&nbsp;pointer")<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>//这里确保target是一个接口或者实现了error接口<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;e&nbsp;:=&nbsp;typ.Elem();&nbsp;e.Kind()&nbsp;!=&nbsp;reflectlite.Interface&nbsp;&amp;&amp;&nbsp;!e.Implements(errorType)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>panic(<span>"errors:&nbsp;*target&nbsp;must&nbsp;be&nbsp;interface&nbsp;or&nbsp;implement&nbsp;error")<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;targetType&nbsp;:=&nbsp;typ.Elem()<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>for&nbsp;err&nbsp;!=&nbsp;<span>nil&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>//关键部分,反射判断是否可被赋予,如果可以就赋值并且返回true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>//本质上,就是类型断言,这是反射的写法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;reflectlite.TypeOf(err).AssignableTo(targetType)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;val.Elem().Set(reflectlite.ValueOf(err))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;<span>true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>//这里意味着你可以自定义error的As方法,实现自己的类型断言代码<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;x,&nbsp;ok&nbsp;:=&nbsp;err.(<span>interface{&nbsp;As(<span>interface{})&nbsp;<span>bool&nbsp;});&nbsp;ok&nbsp;&amp;&amp;&nbsp;x.As(target)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;<span>true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>//这里是遍历error链的关键,不停的Unwrap,一层层的获取err<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;Unwrap(err)<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;<span>false<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>这是<code>As</code>函数的源代码,看源代码比较清晰一些,我在代码里做了注释,这里就不一一分析了,大家可以结合注释读一下。</p>
<h2>旧工程迁移</h2>
<p>新特性的更新,如果要使想使用,不免会有旧项目的迁移,现在我们就针对几种常见的情况看如何进行迁移。<br>如果你以前是直接返回err,或者通过如下方式给err增加了额外信息。</p>
<pre><code><span>return&nbsp;err<br><br><span>return&nbsp;fmt.Errorf(<span>"more&nbsp;info:&nbsp;%v",&nbsp;err)<br></span></span></span></code></pre>
<p>这2种情况你直接切换即可。</p>
<pre><code><span>return&nbsp;fmt.Errorf(<span>"more&nbsp;info:&nbsp;%w",&nbsp;err)<br></span></span></code></pre>
<p>切换后,如果你有<code>==</code>的error判断,那么就用<code>Is</code>函数代替,比如:</p>
<p>旧工程</p>
<pre><code><span>if&nbsp;err&nbsp;==&nbsp;os.ErrExist<br></span></code></pre>
<p>新工程</p>
<pre><code><span>if&nbsp;errors.Is(err,&nbsp;os.ErrExist)<br></span></code></pre>
<p>同理,你旧的代码中,如果有对error进行类型断言的转换,就要用<code>As</code>函数代替,比如:</p>
<p>旧工程</p>
<pre><code><span>if&nbsp;perr,&nbsp;ok&nbsp;:=&nbsp;err.(*os.PathError);&nbsp;ok&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println(perr.Path)<br>}<br></span></code></pre>
<p>新工程</p>
<pre><code><span>var&nbsp;perr&nbsp;*os.PathError<br><span>if&nbsp;errors.As(err,&nbsp;&amp;perr)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println(perr.Path)<br>}<br></span></span></code></pre>
<p>如果你自己自定义了一个<code>struct</code>实现了<code>error</code>接口,而且还嵌套了<code>error</code>,这个时候该怎么适配新特性呢?也就是我们上面举例的情况:</p>
<pre><code><span>type&nbsp;MyError&nbsp;<span>struct&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;error<br>&nbsp;&nbsp;&nbsp;&nbsp;msg&nbsp;<span>string<br>}<br><br><span><span>func&nbsp;<span>(e&nbsp;*MyError)&nbsp;<span>Error<span>()&nbsp;<span>string&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;e.err.Error()&nbsp;+&nbsp;e.msg<br>}<br></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>其实对于这种方式很简单,只需要给他添加一个<code>Unwrap</code>方法就可以了,让它变成一个wrap error。</p>
<pre><code><span><span>func&nbsp;<span>(e&nbsp;*MyError)&nbsp;<span>Unwrap<span>()&nbsp;<span>error&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;e.err<br>}<br></span></span></span></span></span></span></span></code></pre>
<p>这样就可以了。</p>
<h2>结语</h2>
<p>这篇文章深度剖析了Error Wrapping的背景、实现原理以及工程迁移的注意事项,这个新特性是很值得使用的,毕竟增强了error,提供了强大的跟踪能力,再结合一些额外的数据,可以让我们调试、变成会更方便。</p>
<p>&nbsp;</p>
<p>原文:https://www.flysnow.org/2019/09/06/go1.13-error-wrapping.html</p>
</div>

</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:sunsky303,转载请注明原文链接:https://www.cnblogs.com/sunsky303/p/11571440.html</p><br><br>
来源:https://www.cnblogs.com/sunsky303/p/11571440.html
頁: [1]
查看完整版本: Go语言(golang)新发布的1.13中的Error Wrapping深度分析