<p>I don't mind that Go largely forces you to handle errors. I think that's good practice. But I do mind that despite forcing you to handle errors, it doesn't leave you <em>able</em> to handle errors.</p>
<p>The <code>error</code> type is nothing but an interface to a function that returns a string. So by default, I have nothing but a string to help me debug. What if lots of things can go wrong, and I want to react differently to all of them? Is Go encouraging me to use strings in a pseudo-reflective manner, comparing them in if-else statements like exceptions, akin to a design choice a beginner programmer would make? These error messages cannot carry other useful information. I could use a type switch to cast the errors to the underlying struct types which held more information (perhaps a stack trace or a line number or anything else), but no, Go encourages using the <code>error</code> interface type for everything. I can't do this for anything except code I myself have written, unless I go into the implementation code and check out all the errors everything can return. This violates encapsulation!</p>
<p>So all I have is a string, and who can say whether that string will be useful to me? The other day, while trying to decode an ICMP message, I was receiving an error message that said simply <code>invalid argument</code>. That was it, no word on what the invalid argument was. Because Go's error types propagate up the stack without leaving word of where they originated or the places they have passed through, I looked through the library code trying to find where the error originated, and that <em>still</em> didn't tell me anything. The problem was that the program was trying to interpret the packet as IPv4 instead of ICMP, because I had passed an incorrect protocol number constant to the function.</p>
<p>Come to think of it, you're not even forced to handle errors. You can just ignore them with <code>_</code> and be on your way. Or, as is <em>very</em> common, you can decide it's the caller's problem and pass it up the stack.</p>
<p>TL;DR When something goes wrong, I want to know the full 5 <em>w</em>s of it. Go doesn't provide that.</p>
<hr/>**评论:**<br/><br/>quiI: <pre><p>In Go, the interface system allows you to easily construct useful error types whilst implementing the error interfaces so it works with useful functions from the stdlib. </p>
<pre><code>type MyTypedError struct {
UsefulInfo1 string
UsefulInfo2 int
}
func (m *MyTypedError) Error() string {
return "blah"
}
</code></pre>
<p>When you receive an error from a library you can then make a type assertion and get the useful info you need.</p>
<p>You can also have known errors as values and compare for them. </p>
<p>I suggest you read the following: </p>
<ul>
<li><a href="https://blog.golang.org/errors-are-values" rel="nofollow">https://blog.golang.org/errors-are-values</a></li>
<li><a href="http://blog.golang.org/error-handling-and-go" rel="nofollow">http://blog.golang.org/error-handling-and-go</a></li>
</ul>
<p>That's not to say all libraries out there <em>actually</em> return useful errors, but that's true of any PL (how many Java libraries just <code>throw new Exception("derp")</code> ?). Using a library that has crappy errors? Fix it :) </p></pre>Partageons: <pre><p>Did you read my post? If I'm dealing with lots of library code that can return multiple errors, I can't rely on type-casting because I don't know what the type is. I need more by default than a string. Besides, I shouldn't have to write all this myself.</p>
<p>I had read both of those articles before making this post. They don't change a thing about my complaints.</p>
<p>As far as I could tell, the error I described originated in the standard library. How am I supposed to change that?</p></pre>synalx: <pre><blockquote>
<p>I can't rely on type-casting because I don't know what the type is.</p>
</blockquote>
<p>How are you going to handle an error if you don't know what kind of error it is?</p></pre>Partageons: <pre><p>That's exactly what I'm saying.</p></pre>synalx: <pre><p>I think you're missing the point of my question. If you don't know what types of errors could be returned from a library function that you call, how can you expect to do anything reasonable with them besides just return them up the stack, log them, or panic? When you call a library. function that returns <code>error</code>, you should always know what types of errors might come back. That information is part of the API of the library. For example, the <code>os</code> package declares <a href="https://golang.org/pkg/os/#ErrInvalid" rel="nofollow">a few errors it can return</a>.</p>
<p>So you should always know the type(s) of errors that could come back, and a <code>switch err.(type)</code> is a reasonable way to extract specific information from them (where the library provides it).</p></pre>shovelpost: <pre><blockquote>
<p>Besides, I shouldn't have to write all this myself.</p>
</blockquote>
<p>This clearly illustrates the "problem" all this complaining is about.</p></pre>TheMerovius: <pre><p>The kinds of errors that could be returned a part of the API and as such they are potentially subject to changes. i.e. if a developer wants you to be able to distinguish errors, they will document which errors could be returned and what they mean. If they only offer you the error interface without further information, then they want to keep them an implementation detail and by not treating the error as opaque, you are violating encapsulation and potentially make yourself vulnerable to subtle bugs when the API breaks.</p>
<p>If the developer of a package only gives you an error, then that's because that's all they want you to have. Take for example the <a href="https://godoc.org/net/http#pkg-variables" rel="nofollow">http-package</a> -- they clearly want you to know some of the errors that can happen, so they give you the ability to explicitly check for them. They don't want you to handle other errors, because other errors are implementation details and subject to change.</p>
<p>Frankly, I don't see any real difference to other languages. No matter if Java or Python or anything else with exceptions: You catch exceptions by type or by catch-all and if something changes the exceptions they throw, you are pretty much fcucked.</p></pre>Akkifokkusu: <pre><p>There are three ways of handling this:</p>
<ul>
<li><p>The most basic is, yes, using <code>Error()</code> and doing string matching. It's not pretty, but it's often enough depending on the situation.</p></li>
<li><p>The second also involves comparison, but not of the string representation. For simple errors, many libraries will export error variables and document which functions might return those errors. (See <a href="https://godoc.org/golang.org/x/net/context#pkg-variables" rel="nofollow"><code>context</code></a>.) In that case, a simple <code>if</code> or <code>switch</code> will suffice.</p></li>
<li><p>Lastly:</p>
<blockquote>
<p>Go encourages using the <code>error</code> interface type for everything</p>
</blockquote>
<p>Yes, it's standard practice to return <code>error</code> and allow the consumer to decide what to do with it. But there's a reason it's an interface instead of a struct or string type.</p>
<p>Similar to exporting variables, many libraries that return more complex errors will export the underlying types for those errors and document which functions might return errors of those types. The whole point of doing that is that, if needed, the consumer can use a type switch or type assertion to expose and inspect the underlying value. Far from being discouraged, <code>error</code> as an interface means errors handling is extremely flexible.</p></li>
</ul>
<p>Of course, the latter two solutions require the library being used to be helpful. But as <a href="/u/quiI" rel="nofollow">/u/quiI</a> mentioned, that's not so different from errors/exceptions in other languages.</p></pre>__crackers__: <pre><blockquote>
<p>Or, as is very common, you can decide it's the caller's problem and pass it up the stack.</p>
</blockquote>
<p>What's the problem with this? Perhaps it's just what I'm used to from Python, but it seems to me that my library code can't know how your application wants to handle errors.</p>
<p>Sure, I'll try to tidy up any crap that my code may have left lying around before bugging out, but it seems to me that deciding what to do in the case of an error is the calling code's business.</p></pre>neoasterisk: <pre><blockquote>
<p>unless I go into the implementation code and check out all the errors everything can return. This violates encapsulation!</p>
</blockquote>
<p>How does it violate encapsulation? If a package has defined a few errors like <code>ErrNotExist</code> staring with capital then it's an exported variable which you are meant to check for. It's basically part of the public API of the package. The same way you are using the packages's functions, the same way you use it's exported errors. Read the documentation of the package and you might not even have to check the code.</p>
<p>I really cannot understand where the problem is.</p></pre>dchapes: <pre><p>A year ago I wrote a <a href="http://stackoverflow.com/a/30178766/55504" rel="nofollow">StackOverflow answer</a> that describes several ways a package can use to report errors. If a package uses ad hoc <code>error.New</code> instead of exported variables/types for errors you expect to test for, then it's a deficiency/bug in that package and you should submit an issue and/or fix your own vendored version of that package.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传