Small utility function for auto closing `io.Closer`

agolangf · · 77 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Just wanted to share a small utility function which can be used to automatically close files etc. and also handle the error of the <code>Close</code> method. Most of the time people do not check the closer error because it&#39;s mostly called with a <code>defer</code> statement in front of it, ignoring the error it can generate. </p> <pre><code>func With(closer io.Closer, f func() error) (err error) { if closer == nil { return nil } defer func() { err = closer.Close() }() return f() } </code></pre> <p>Example usage:</p> <pre><code>f, err := os.Open(&#34;main.go&#34;) if err != nil { panic(err) } content := []byte{} err = With(f, func() error { content, err = ioutil.ReadAll(f) return err }) </code></pre> <hr/>**评论:**<br/><br/>SeerUD: <pre><p>I like the fact it handles the error, but I&#39;m not sure that I like that it would hide the closing from me in a function. I like being able to skim read and check for things like this, and it&#39;d trip me up probably in things like code reviews.</p></pre>saturn_vk: <pre><p>That seems like it would erase any error returned by the closure</p></pre>jerf: <pre><p>It will. To do this safely either requires the use of some sort of multierror-capable struct, or in the simplest way to do it, returning two errors.</p> <p>As it happens just yesterday a coworker and I were looking over some code where we had to have two separate error objects in a particular function, because we had a place that could generate an error, which would then require some cleanup which could itself fail, and we needed both errors farther down for proper handling (including, but non limited to, logging properly). It doesn&#39;t happen often in my experience but it definitely happens.</p></pre>forfunc: <pre><p>I agree. The close error is prioritised before the closure, maybe some error wrapping would solve this. Note that I don&#39;t write code like this often, it was just an inspiration when reading through some python docs. (especially the with statement).</p></pre>TheMerovius: <pre><p>IMO if you want a helper, it makes more sense to have this helper:</p> <pre><code>func Close(c io.Closer, err *error) { if e := c.Close(); *err == nil { *err = e } } f, err := os.Open(&#34;main.go&#34;) if err != nil { panic(err) } defer Close(f, &amp;err) </code></pre> <p>Less indent, less callbacks, more idiomatic. But, honestly, just <code>defer f.Close()</code>. Most, if not all, Closers deal fine with having <code>Close</code> called twice. So in &gt;99% of cases there&#39;s no issue in doing something like</p> <pre><code>f, err := os.Open(filename) if err != nil { return err } defer f.Close() if err := doThingWith(f); err != nil { return err } return f.Close() </code></pre> <p>Which is clean, idiomatic code, does what it&#39;s supposed to and checks the error as expected.</p></pre>kostix: <pre><p>…except that one always must handle errors when closing files <em>opened for writing.</em></p> <p>Please see the <code>close(2)</code> manual page for more info.</p> <p>(Not that I find the OP&#39;s helper to be useful anyway.)</p></pre>TheMerovius: <pre><blockquote> <p>…except that one always must handle errors when closing files opened for writing.</p> </blockquote> <p>My code does check the error. [edit] though it contained a mistake, in that I assigned <code>c.Close</code>, not e, which I now corrected ^^[edit]</p> <p>(Also, <code>(*os.File).Close</code> does not call <code>close(2)</code> directly, but instead goes through the netpoller. Which is why it&#39;s safe to call it twice, AIUI)</p></pre>soapysops: <pre><p>That&#39;s actually a common misconception. It&#39;s almost never useful to check the result of <code>Close()</code> in Go code (or in other languages for that matter).</p> <p>Here&#39;s why: if you care at all about whether or not a file has been written to the disk correctly, you <em>must</em> call <code>Sync()</code> before calling <code>Close()</code>. Because the OS might try writing a file to the disk after <code>Close()</code> is called, and it could then fail, and you&#39;d have no way of knowing.</p> <p>I can see why someone would get the opposite impression from Linux&#39;s man page for <code>close(2)</code>. It explains that the error result from <code>close</code> is not what you need if you actually care about the error in the first paragraph of the NOTES section, but then it goes on to say in a semi-condescending way that <em>careful</em> programmers always check the result of <code>close(2)</code>, nearly contradicting itself in a few places.</p></pre>
77 次点击  
加入收藏 微博
0 回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传