<p>Hello all! I'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'd really appreciate having advice from people more experienced than myself.</p>
<p>When I'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 < fileData.byteCount {
return ErrNeededMoreSpace (fileData.bytecount - system.disk.freeSpace)
}
if system.disk.writeLocked {
return ErrDiskWriteLocked
}
bytesWritten := system.file.write(fileData, "foo.jpeg")
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("Free up %d bytes please", x)
}
}
</code></pre>
<p>My problem in Go is that I can'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't seem useful for figuring out what <em>type of error</em> happened. I've seen people instantiate something like <code>errDiskWriteLocked = errors.new("Disk was write locked")</code> at the start of a file and just return references to the specific instance, but then you can't really attach helpful information to the error. Other times, I'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'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'll have to make your own type (that implements <code>error</code>). There'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's great! Sounds like robust software. But there'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'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'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("not enough space (need %vb more)", 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("%d bytes written", 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
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传