Ask /r/golang: What are the legitimate cases for using panic() / recover() calls?

blov · · 487 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<hr/>**评论:**<br/><br/>FUZxxl: <pre><p>There are two use cases for <code>panic()</code>:</p> <ul> <li><code>panic()</code> when an error condition occured that should never occur in a normal use of your module.</li> <li><code>panic()</code> as an exception mechanism to escape from deeply nested stack frames. Such a <code>panic()</code> should not cross module boundaries.</li> </ul> <p>Good situations where the first one applies are:</p> <ul> <li><code>nil</code> was passed where a pointer was expected</li> <li>an internal invariant doesn&#39;t hold or a consistency check fails</li> <li>a case was reached that should be unreachable</li> </ul> <p>Situations where the first one does not apply are:</p> <ul> <li>A configuration file is missing or malformed</li> <li>The function was called with incorrect arguments</li> <li>A network connection was lost</li> </ul></pre>peterbourgon: <pre><p>I largely agree with <a href="/u/FUZxxl">/u/FUZxxl</a>, panics are ~exclusively for programmer errors e.g. invariant failures. You can also use panic/recover for error control flow in certain <em>very limited</em> circumstances, like building a recursive descent parser. encoding/json is one example.</p></pre>cobratbq: <pre><p>I was wondering the same thing a while back. Right now, I use it in every case where the caller is simply using the function wrongly. With wrong I mean literally, this can never succeeded, this is faulty code. This does not include cases where the method fails because the object is in the wrong state, as state might be manipulated by outside influences and the caller might not know of the state change beforehand (e.g. race in networking protocol).</p> <p>An innocent question that led to quite a elaborate discussion can be found here: <a href="https://groups.google.com/forum/#!searchin/golang-nuts/%28master%29/golang-nuts/Qg4CNgC7RPY/KWyWPD3ACVYJ" rel="nofollow">https://groups.google.com/forum/#!searchin/golang-nuts/%28master%29/golang-nuts/Qg4CNgC7RPY/KWyWPD3ACVYJ</a> It might be an interesting read as a lot of people share their views on this.</p> <p>In general, I think the go team takes the approach I described above. (However, please correct me if I am wrong on this.)</p></pre>FUZxxl: <pre><p>I don&#39;t think API misuse should cause a panic in all cases. I usually only panic on API misuse when the specific API provides no other mean of reporting failure.</p></pre>jahayhurst: <pre><ul> <li>There&#39;s one rule to not break - Don&#39;t cross modules/packages - a panic should not escape a package.</li> <li>The &#34;textbook&#34; guideline I&#39;ve heard is to use panic/recover within your package for situations abnormal entirely within your package - everything else is nominal, but you still hit an error in your package.</li> <li>More generally, it&#39;s up to you, or the code you&#39;re writing - let it express itself in whichever way it&#39;s more natural. Just like mutex lock vs channels. I often use errors inside my packages. Maybe I shouldn&#39;t? idc, they&#39;re a bit nicer I think, and I can build the error on the way out.</li> </ul></pre>TheMerovius: <pre><p>I so far found one legitimate use of panic/recover that can&#39;t work without it: Closing a channel that might already be closed</p> <pre><code>func Close(ch chan int) { defer func() { recover() }() close(ch) } </code></pre></pre>peterbourgon: <pre><p>Wanting to close a channel that might already be closed is <em>always</em> a problem in your program design.</p></pre>TheMerovius: <pre><p>I strongly disagree. For example if I offer some resource and want to have an idempotent Close() (IMHO, Close() should <em>always</em> be idempotent). If your resource needs to shut down other goroutines you need to signal the Close() somehow and the easiest way is closing a done-channel. Above gives you a goroutine-safe idempotent way to do this in just a handful of lines of code.</p> <p>Offering a clean and not unnecessarily hard to use API is a nice thing in my book and I don&#39;t see anything wrong with using above code to express this clearly. I see no reason to be a purist here except purity as an end in itself.</p></pre>earthboundkid: <pre><p>Then use sync.Once. </p></pre>TheMerovius: <pre><p>Or just write that one line of code (though I acknowledge that I didn&#39;t think about sync.Once. But it also needs more code for no benefit here).</p> <p>Seriously, what is the problem with that line of code. I would like to hear an argument that isn&#39;t a) &#34;I don&#39;t like recover&#34; or b) &#34;I don&#39;t like closing channels twice&#34;. It is obvious, it is correct and it does what it&#39;s supposed to do.</p> <p>All these purisms have a <em>practical</em> usefulness, but this doesn&#39;t apply here. They make it easier to debug, they make code more readable, they make it easier to write it correct. But none of those apply here.</p> <p>We shouldn&#39;t blindly trying to be pure, just for the sake of it.</p></pre>earthboundkid: <pre><p>That&#39;s a fair point. If I had more time, I might like to benchmark the two solutions just for comparison.</p></pre>earthboundkid: <pre><p>I wrote a quick benchmark: <a href="http://play.golang.org/p/pha66htq-e" rel="nofollow">http://play.golang.org/p/pha66htq-e</a></p> <pre><code>BenchmarkCloseOnce-4 200000000 7.63 ns/op BenchmarkClosePanic-4 10000000 210 ns/op </code></pre> <p>FWIW.</p></pre>dchapes: <pre><p>A slightly better benchmark: <a href="https://play.golang.org/p/LTQ12eSV-c" rel="nofollow">https://play.golang.org/p/LTQ12eSV-c</a><br/> This includes the time to open and actually close instead of just the nop closes.</p> <pre><code>BenchmarkOpenCloseOnce 5000000 345 ns/op 128 B/op 2 allocs/op BenchmarkOpenClosePanic 5000000 370 ns/op 112 B/op 2 allocs/op </code></pre></pre>dsymonds: <pre><p>An idempotent Close is the exception, not the rule. For instance, see the documentation for io.Closer, which is implemented by a lot of types in the standard library.</p></pre>TheMerovius: <pre><p>It might be the exception, but it is both a valid usecase and a nice thing to offer. Especially when only a single line of code is needed to solve it elegantly and quickly in your abstraction instead of the level above.</p></pre>nhooyr: <pre><p>you should read this <a href="https://blog.golang.org/pipelines" rel="nofollow">https://blog.golang.org/pipelines</a></p></pre>TheMerovius: <pre><p>I have. Multiple times.</p></pre>gbitten: <pre><p>Besides their normal use, I use them as a simple way to escape from nested functions in a web applications. For example, to implement HTTP redirection with a simple syntax.</p></pre>earthboundkid: <pre><p>I think it&#39;s better to have a controller that can return an error and make an error for 404, 301, etc. that the outer controller uses. </p></pre>gbitten: <pre><blockquote> <p>I think it&#39;s better to have a controller that can return an error and make an error for 404, 301, etc. that the outer controller uses.</p> </blockquote> <p>That is what I&#39;m doing, but I use panic/defer/recover calls to simplify the controller.</p></pre>headzoo: <pre><p>I use <code>panic()</code> on programmer errors, not application errors. It&#39;s the &#34;logic error&#34; used by other languages, which should be found during development.</p> <p>In other words, I use <code>panic()</code> to find bugs that should never be bugs again once they&#39;ve been found and fixed. These are the types of bugs that cause subtle problems in your application if they go unnoticed, so you want these types of bugs to stop everything and wave a big red flag saying, &#34;Here I am! Fix me!&#34;</p> <p>I don&#39;t use <code>recover()</code> very often. Usually only when I&#39;m executing a bunch of &#34;tasks&#34; in a pool of goroutines, and I don&#39;t want the failure of one task to stop the others from completing.</p></pre>frou: <pre><p>Inside an interpreter for a Lisp-like language, I used panic+recover as a way to transport a request by $LANG code to do an efficient tail-recursive procedure call.</p> <p>Basically as an &#34;out of band&#34; way to chuck something up the (Go) stack that wasn&#39;t an error.</p></pre>

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

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