A beginner's question about how to work with Golang errors

agolangf · · 442 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hello all! I&#39;m new to Go and really enjoying it so far, but this one area has made me a bit confused about the best way to work and I&#39;d really appreciate having advice from people more experienced than myself.</p> <p>When I&#39;m writing a function that does something that has the potential to fail, I want to think about the ways it might fail, check for them, and then return an error that describes exactly what happened and why.</p> <p>Some rough pseudocode to show how I might want to write something.</p> <pre><code>type WriteStatus = ErrDiskWriteLocked | ErrNeededMoreSpace int | AllGood int function WriteFileToDisk(fileData) WriteStatus { if system.disk.freeSpace &lt; fileData.byteCount { return ErrNeededMoreSpace (fileData.bytecount - system.disk.freeSpace) } if system.disk.writeLocked { return ErrDiskWriteLocked } bytesWritten := system.file.write(fileData, &#34;foo.jpeg&#34;) return AllGood bytesWritten } </code></pre> <p>Then, when I call my function, I want to be able to check what type the error had, eg</p> <pre><code>function SaveImage() { status := writeFileToDisk(webcam.imageCapture) if status == ErrDiskWriteLocked { system.disk.unlockDisk return SaveImage() } if status == ErrNeededMoreSpace x { log.Fatalf(&#34;Free up %d bytes please&#34;, x) } } </code></pre> <p>My problem in Go is that I can&#39;t quite figure out how I should give a label to an error and attach data to it. <code>errors.New</code> only creates a generic error with a string message, so it doesn&#39;t seem useful for figuring out what <em>type of error</em> happened. I&#39;ve seen people instantiate something like <code>errDiskWriteLocked = errors.new(&#34;Disk was write locked&#34;)</code> at the start of a file and just return references to the specific instance, but then you can&#39;t really attach helpful information to the error. Other times, I&#39;ve seen people create their own custom types for errors with methods on them, the <code>Error()</code> method making it fit the interface -- but then reflecting to figure out what type the error is is still a clunky, messy-feeling multi-step operation. Go By Example lists multiple options but advises none of them as the clean, simple, friendly option.</p> <p>So my question is: how would I nicely represent and pass errors in a situation like my example? How can I create an error object that simply expresses what went wrong and passes helpful information without making it a burden to check every time?</p> <hr/>**评论:**<br/><br/>FermentingPotato: <pre><p>I highly recommend reading this <a href="https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully" rel="nofollow">post on errors</a></p></pre>quiI: <pre><p>Coincedentally I am writing something about this right now (WIP)</p> <p><a href="https://github.com/quii/learn-go-with-tests/blob/master/pointers/readme.md#write-enough-code-to-make-it-pass-2" rel="nofollow">https://github.com/quii/learn-go-with-tests/blob/master/pointers/readme.md#write-enough-code-to-make-it-pass-2</a></p> <blockquote> <p>Other times, I&#39;ve seen people create their own custom types for errors with methods on them, the Error() method making it fit the interface -- but then reflecting to figure out what type the error is is still a clunky, messy-feeling multi-step operation.</p> </blockquote> <p>AFAIK, if you want a way of attaching your own info you&#39;ll have to make your own type (that implements <code>error</code>). There&#39;s literally no other way to pass information around. </p> <p>re the type assertion. It looks like you have multiple ways that it can fail and you want the caller of your code to have to handle it. That&#39;s great! Sounds like robust software. But there&#39;s very little syntactic sugar in Go, the language values explicitness in these scenarios. </p> <p>I can imagine a <code>switch</code> on people calling your function, checking the type of the error and then acting accordingly. </p> <p>It isn&#39;t clever or elegant, but it is very clear.</p> <p>For lazy developers, they can still just treat your error lazily if they just want to log it or whatever.</p></pre>user3961: <pre><p>Haven’t tried this, but something like </p> <p>type RecoverableError interface{ Error() sting Recover() }</p> <p>type Error1 struct{ error X context.Context // just some attached data }</p> <p>type Error2 struct{ error Y context.Context // just some attached data }</p> <p>func (err1 Error1) Recover() { // use data in err1 to handle itself }</p> <p>func (err2 Error2) Recover() { // }</p> <p>You get .Error() for free</p> <p>Easy to wrap errors and attach some context to it</p> <p>When handling you’ll have to type switch or reflect </p> <p>Edit: I don’t like this answer either. I actually made a yaml formatter for logrus that I attach data to when I need to. It’s pretty sweet i should share it, colorized, hijacks grpclog, if no data is attached it prints the one liner. </p></pre>akavel: <pre><p>Generally, reflecting on types in Go is not scorned upon so much as in some other languages. In fact, there are cases when it is seen as <em>idiomatic code!</em> In particular, in this case, I&#39;d totally go with creating a custom type — probably something like:</p> <pre><code>type ErrNeedMoreSpace struct { Bytes int64 } func (e ErrNeedMoreSpace) Error() string { return fmt.Sprintf(&#34;not enough space (need %vb more)&#34;, e.Bytes) } </code></pre> <p>See also, in the standard library:</p> <ul> <li><a href="https://golang.org/pkg/os/#IsNotExist" rel="nofollow">os.IsNotExist()</a></li> <li><a href="https://golang.org/src/os/error.go" rel="nofollow">os/error.go</a></li> <li><a href="https://golang.org/src/os/error_unix.go" rel="nofollow">os/error_unix.go</a></li> </ul> <p>In particular, your code could then become more or less:</p> <pre><code>func SaveImage() { n, err := writeFileToDisk(...) if err != nil { if err == errDiskWriteLocked { ... } log.Fatal(err) } log.Printf(&#34;%d bytes written&#34;, n) } </code></pre> <p>without even needing the type cast because of good .Error()... but if you really needed it:</p> <pre><code>if err := err.(ErrNeedMoreSpace); err != nil { // do something special... } </code></pre></pre>govision: <pre><p>I think these videos explain a lot.</p> <p><a href="https://youtu.be/hDC7IF3OQLM" rel="nofollow">https://youtu.be/hDC7IF3OQLM</a></p> <p><a href="https://youtu.be/1B71SL6Y0kA" rel="nofollow">https://youtu.be/1B71SL6Y0kA</a></p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

442 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传