<p>I've been using Go for little over a year now and still the one thing which grates on at me is error handling. Every time I use a project which needs it (almost every one), I end up creating a utility method accepting a theoretical "try" block as a func and a "catch" block as a func. The issue is, this looks ugly and turns code into some try-catch-spaghetti.</p>
<p>Is there a better way of handling this yet? I haven't kept up to date on language specifications and wasn't even aware 1.8 was released, so just holding out hope here.</p>
<hr/>**评论:**<br/><br/>quiI: <pre><p>Why do you feel the need for the code you're describing, got a more concrete example?</p>
<p>FWIW my view of error handling is.</p>
<ul>
<li>If a function can fail, it's nice that the type signature tells me so</li>
<li>Therefore the language "forces" me to handle the error in some way. Great! I have lived with C#/Java codebases with exceptions being slung around confusing everyone, they suck.</li>
<li>How do I handle an error?</li>
</ul>
<p>1) If i can actually handle it, i.e i know how to recover from it and do something good, then write the code!</p>
<p>2) If I cant handle it, add some context and return it</p>
<pre><code>user, err := service.GetUser(id)
if err != nil {
return fmt.Errorf("problem getting user %s so i cant promote: %v, id, err)
}
promoteUser(user)
</code></pre>
<p>This results in explicit, easy to understand code. If it fails, the logs give me a nice <em>context sensitive</em> message so i can debug it.</p></pre>fabsterpal: <pre><p>I was actually talking about when libraries force a panic on you. I don't particularly like the return-error model, but it's a lot easier to handle than what I'm talking about so I don't really mind it.
What I'm talking about is if a library were to do...</p>
<pre><code>if (cakes < 0) {
panic("not enough cakes")
}
</code></pre>
<p>That then means I have the tedious task of heading my accessing function with</p>
<pre><code>defer func() {
if err := recover(); err != nil {
// do something to handle err
}
}()
</code></pre>
<p>Which is quite the bootstrap code. Especially when this only works on a func level, it doesn't go up the call trace hierarchy.</p>
<p>Was wondering if there's a better way to handle panics, without crashing the entire goroutine.</p></pre>peterbourgon: <pre><p>Libraries should effectively never panic on you. Which library is doing that?</p>
<p>edit: I've been programming Go for 7 years, and I can count the times I've used <code>recover</code> on 1 finger.</p></pre>fabsterpal: <pre><p>I've encountered it with quite a few, none come to mind. I asked this question as I'm about to start another project and wanted to know if there's some trick I don't yet know.</p></pre>peterbourgon: <pre><p>I'm calling shenanigans. I'm not aware of any widely-used library that panics across API boundaries to signal errors. Panic recovery simply isn't something you should ever feel the need to do.</p></pre>fabsterpal: <pre><p>Not everybody follows the same standards as the standard library, that is, returning an error. Sometimes it's not even the API's fault - sometimes they just don't handle nil pointer checking or errors on internal processes and that causes the VM to panic itself.</p></pre>peterbourgon: <pre><p>Not panicking isn't some narrow standard of the stdlib, it's a common standard for being a well-behaved Go library. Obviously not all code is well-behaved, but we should treat those libraries as broken and fix them, not shrug and compensate for their failures in our own code.</p></pre>dilap: <pre><p>Panicking across a library boundary is <em>extremely</em> bad form. Encourage the library author to change this, or fork and fix it yourself.</p></pre>dchapes: <pre><blockquote>
<p>they just don't handle nil pointer checking or errors</p>
</blockquote>
<p>These don't need to be checked and often shouldn't be. Just as if you are doing <code>slice[i]</code> when <code>i</code> is out of bounds it is a programming error, calling a package function/method with invalid input is a programming error that should panic. It's the callers responsibility to make sure it's calling things in a valid way.</p>
<p>(E.g. a type that is initialised via a filename that doesn't exist should return an error but calling a close method on an un-initialised such type should just panic; it's a programming error for a caller to do something like <code>f, _ := os.Open("foo"); f.Close()</code>).</p></pre>cube2222: <pre><p>I never encountered it.</p></pre>kostix: <pre><p>If the library we're talking about panics for in "normal" cases—that is, in cases different from you calling one of its functions with a value outside of the valid domain for its particular argument,—the library can be said to go against the grain of <a href="http://stackoverflow.com/a/22865084/720999" rel="nofollow">how Go libraries have to be written</a>, and so I'd recommend to just file a bug against the library asking the author to reconsider what they do.
In the same venue, please squint at your own code, too: if you were not supposed to call that library's function passing it the number of cakes less than 0, it's your fault, not the library's.</p>
<p>Another approach is to wrap the library in your own shim and trap all possible panics in the shim's methods/functions.
I know this stinks but at least you'll be suffering this pain once, and then would just use the shim everywhere in your code not being afraid it could blow up on you.</p>
<p>Yet another approach—if feasible in your situation—is to create a custom wrapper function to run goroutines the code in which is to make use of that faulty library. Sure, I'd work only if you're okay with handling those panics at the top level rather than on the spot.</p></pre>: <pre><p>[deleted]</p></pre>kostix: <pre><p>As I've tried to highlight, it depends on the essense of the panic.</p>
<p>If a library exports a function like <code>ParseFile(fname string)</code> it must not panic on inability to open or read the indicated file or if it encounters a problem with the file format.</p>
<p>If a library exports a <code>type Foo struct { ... }</code> and a method <code>func (f *Foo) Frobnicate { ... }</code> defined on a pointer of <code>Foo</code>, and you call it like <code>var foo Foo = nil; foo.Frobincate()</code>, it's absolutely OK for that call to panic—even if it happens not right at the spot but somewhere deeper down the call chain (which <code>Foo.Frobincate()</code> would perform).</p>
<p>To take your example, if <code>reflect</code> panics when you call one of its functions with something it was not supposed to be called with, it's OK for it to panic. Otherwise it's a bug in the package (which, I admit, is highly unlikely).</p>
<hr/>
<p>To elaborate on my <code>ParseFile()</code> example, the API of the Go stdlib which is explicitly allowed to panic is explicitly marked as such—say, <code>template/*</code> packages and the <code>regexp</code> package have helper routines which <em>assert</em> what you pass to them is correct, panic if this invariant does not hold, and so they named like <code>MustParse()</code> and <code>MustCompile()</code> to highlight their behaviour.</p>
<p>Note that the whole reason they exist is to deal with <em>static</em> inputs which just can't be sensibly "precompiled" but are otherwise treated as such: templates shipped with the binary and regular expressions which are part of your algorytm (as opposed to having been input by the user interactively) are perfect examples of this. IOW, you can't make the compiler typecheck them but you tell the runtime "trust me, I know what I'm passing in is correct, so just blow up if it isn't".</p></pre>fabsterpal: <pre><p>The types of issues I'm talking about are both the occasion when a library panics itself explicitly and when it doesn't handle every possible failure such as your example of ParseFile not being able to open/read/parse the file. Libraries that just assume that their check if the file exists is sufficient to skip all pointer checking.</p></pre>DualRearWheels: <pre><p>Those are probably shit libraries. Go is clear on correct style: libraries should never expose panics that user should catch, they should handle it internally and return errors from public functions. Go standard lib is built this way and causes no problems.</p>
<p>Now there are people that use panics as exceptions, out of ignorance, preference or some other reason, so when they write Go libraries you may get problems.</p></pre>fabsterpal: <pre><p>They genuinely are shit libraries, but sometimes they don't raise panics themselves, sometimes it's because they don't internally handle an error or a nil pointer correctly. </p></pre>DualRearWheels: <pre><p>Those are bugs then, so there is nothing else you can do than to recover and cancel what program is doing and notify authors of lib. I wouldn't proceed with action if lib call caused panic.</p></pre>Akkifokkusu: <pre><blockquote>
<p>Especially when this only works on a func level, it doesn't go up the call trace hierarchy.</p>
</blockquote>
<p>Not sure what you mean by this. A <code>panic</code> will propagate up the call stack until it hits a <code>defer</code>red <code>recover()</code>: <a href="https://play.golang.org/p/JGTVANGOfH" rel="nofollow">https://play.golang.org/p/JGTVANGOfH</a></p></pre>shovelpost: <pre><ul>
<li><a href="https://blog.golang.org/error-handling-and-go" rel="nofollow">https://blog.golang.org/error-handling-and-go</a></li>
<li><a href="https://blog.golang.org/errors-are-values" rel="nofollow">https://blog.golang.org/errors-are-values</a></li>
</ul></pre>epiris: <pre><p>Panics are not used as exceptions, not in a single library I have ever seen. Until you provide an example this assertion you are making is false, invalidating your issue altogether.</p>
<p>Regardless they are not implemented nor function like an exception so it doesn't make sense to have machinery to support them. There is no block level stack unwinding like Exceptions. Recover can only be executed in a defer because essentially (simplifying slightly) all it does is change the stack pointer and notify scheduler.</p>
<p>This is nothing like an exception. Exceptions have blocks, they require compilers to be much more complicated because you must jump from nestedblocks, which may or may not escape the current function scope.</p>
<p>So point is, panics are not used like exceptions. Because it's impossible since panics do not have similar behavior. </p></pre>: <pre><p>[deleted]</p></pre>fabsterpal: <pre><p>Panics are used as exceptions but the issue is Go lacks the language features to easily try-catch these. </p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传