Ask Golang: Why does this work with both string and interface?

xuanbao · · 509 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I am learning Golang. I think, it goes without saying that, interfaces are tricky to understand. </p> <p>I am following code example from gofragments which can be found <a href="http://www.gofragments.net/client/blog/fundamentals/2015/09/16/containsElemThisSliceOfStruct/index.html">here</a>.</p> <p>Below line confuses me:</p> <pre><code>func containsCodeHash(items PlaygroundItems, elem interface{}) (bool, int) { </code></pre> <p>If I replace <code>interface{}</code> with <code>string</code>, code works perfectly fine.</p> <p>My questions are:</p> <p>1) why do they work the same way? 2) which one should be preferred over the other and why?</p> <p><strong>EDIT</strong>: Thanks everyone for the comments. I feel more dumb than I used to. :)</p> <hr/>**评论:**<br/><br/>jerf: <pre><p>Actually, I kinda disagree with most of the explanations here. This isn&#39;t about interfaces <em>qua</em> interfaces. This is about how <code>==</code> works, which is <a href="https://golang.org/ref/spec#Comparison_operators">sort of complicated</a>.</p> <p>When <code>elem</code> is a string, you are comparing string to string, and thus you hit the</p> <ul> <li>String values are comparable and ordered, lexically byte-wise. </li> </ul> <p>clause, which means that two strings are <code>==</code> if they are byte-wise identical. Normal stuff.</p> <p>When you set <code>elem</code> to <code>interface{}</code>, you are comparing a <code>string</code> to an <code>interface{}</code>. That means you hit this clause: </p> <ul> <li>A value <code>x</code> of non-interface type <code>X</code> and a value <code>t</code> of interface type <code>T</code> are comparable when values of type <code>X</code> are comparable and <code>X</code> implements <code>T</code>. They are equal if <code>t</code>&#39;s dynamic type is identical to <code>X</code> and <code>t</code>&#39;s dynamic value is equal to <code>x</code>.</li> </ul> <p>That&#39;s a mouthful. Let&#39;s specialize the variables to this specific case:</p> <ul> <li>A value <code>item.CodeHash</code> of non-interface type <code>string</code> and a value <code>elem</code> of interface type <code>interface{}</code> are comparable when values of type <code>string</code> are comparable and <code>string</code> implements <code>interface{}</code>. They are equal if <code>elem</code>&#39;s dynamic type is identical to <code>string</code> and <code>elem</code>&#39;s dynamic value is equal to <code>item.CodeHash</code>.</li> </ul> <p>So:</p> <ul> <li>values of type <code>string</code> are comparable (see previous quoted clause)</li> <li><code>string</code> does indeed implement <code>interface{}</code>, since everything does</li> <li><code>elem</code>s type is indeed string, &#34;behind&#34; the <code>interface{}</code></li> <li>and the two strings are equal (or not as the case may be)</li> </ul> <p>Therefore it works either way. However there can be a runtime difference as the <code>string == interface{}</code> has more work to do, exactly as the additional text implies, if the compiler can&#39;t figure out to optimize it away. (Whether it does in this case I have no idea; you can check it yourself by <a href="http://blog.golang.org/profiling-go-programs">profiling</a>.) </p></pre>bredov: <pre><p>Good answer. Wanna add that you should be always careful when putting constants in interface{}, like it is in gofragments example. It may sometimes lead to non-obvious mistakes due to strict type comparison while comparing with interface{}. <a href="https://play.golang.org/p/HQBQGVBtkM" rel="nofollow">https://play.golang.org/p/HQBQGVBtkM</a></p></pre>jerf: <pre><p>Ugh. I will have to dock a prettiness point from Go for being able to have <code>x == 10</code> and <code>y == 10</code> be true while <code>x == y</code> is false. I understand why that is in Go, and it&#39;s still a lot better than PHP or Perl&#39;s equality checks, but still, that is one step in the direction of evil.</p> <p>(I&#39;ve really come to favor very strict equality checks. One thing Go does that I agree with is that the check in a <code>for</code> or <code>if</code> <em>must</em> be a boolean; <code>if 0</code> is not a legal if statement. Too many years with very &#34;flexible&#34; languages have left me too many times bitten. Though honorable mention to Python for still being at least decent, for a dynamic language, on this front.)</p></pre>bredov: <pre><p>What i would like to see in Go 2 is any operator between interface and constant to be forbidden, unless constant is specified with type. So, this would be illegal</p> <pre><code>var x interface{} = 10 fmt.Println(x == 10) </code></pre> <p>While this will work</p> <pre><code>var x interface{} = int(10) fmt.Println(x == int(10)) </code></pre> <p>Of course you can always hold in mind the type defaults for constants, but this will make it more explicit IMO and will make you always remember to check the type correctness</p></pre>waveswan: <pre><p>I agree. What I wish languages would do is use the == operator for primitive numeric types, with absolutely no automatic type conversion, and then have ~ represent more complex equivalence relations. Essentially, the equal sign should mean that two things are the same, either because they are made equal by an assignment, or because the operator only evaluates to true when they are. </p></pre>TheMerovius: <pre><p>Thanks for this answer :) I never knew you could actually compare interface-types with non-interface types :)</p></pre>TheMerovius: <pre><p>Also, it&#39;s kind of amazing how none of the answers are correct and still got upvoted :)</p></pre>brokedown: <pre><p>In his examples, it works with string because he&#39;s using it with strings. As an interface, it could also accept other types.</p> <p>Generally speaking, if you&#39;re only using it with strings, it&#39;s much more clear to replace the interface type with a string type. If you&#39;re trying to make it worth with multiple types, the interface starts to make sense.</p></pre>DarkAnHell: <pre><p>An interface means &#39;anything that has x in it&#39;. So, an interface with no elements (interface{}) means &#39;anything&#39;, because any type &#39;has nothing&#39; in them.</p> <p>As for which one is better, it&#39;s always best practice to explicitly ask for the type you want (i.e. string), except for when you could accept anything, or several types that match conditions. Then you should use interface.</p> <p>[excuse bad formatting or poor explanation, typing quickly on my phone!]</p></pre>njpatel: <pre><p>It works because you can think of <code>interface{}</code> as the base type for all other types (from simple types such as <code>string</code> or <code>bool</code>, to complex ones that you create yourself).</p> <p>The fact that the method works in the example is because the author knows that they are passing in a string type, even if it&#39;s being transformed to it&#39;s most basic type, and therefore they know the equality check with <code>item.CodeHash</code> will work (as that&#39;s a <code>string</code>).</p> <p>The more complex your program gets, the more chances there are for functions like this to come back and bite you with various bugs. Swapping <code>interface{}</code> for <code>string</code> is much better, and much more explicit. That way, if you write code that sends in a bool or another type into the function, the compiler will refuse to compile the code and you&#39;ll be able to fix your mistake.</p> <p>While that function has <code>interface{}</code> as the param type, the chances are you could pass in a <code>bool</code> or other type and not know that it&#39;s always going to fail silently.</p></pre>sharptierce: <pre><p>interface{} means you can use everything for elem. If you use &#34;elem string&#34;, than the function can only be called with a string parameter. Your example code uses &#34;CodeHash string&#34; so it will only work with string arguments. </p> <p>Writing interface{} here suggests that I could also check if containsCodeHash(items, PlaygroundItem{}), which would not work in your example.</p> <p>I hope I explained it somehow understandable<sup><sup>&#34;</sup></sup></p></pre>pmjtoca: <pre><p>Hi. The function containsCodeHash(items PlaygroundItems, elem interface{}) (bool, int) has 2 input parameters and returns 2 others.</p> <p>the 2nd input parameter &#39;elem&#39; is of type: &#39;interface{}&#39;. An interface is two things: it is a set of methods, but it is also a type. The interface{} type is the interface that has no methods.</p> <p>At runtime, since all values have exactly one type, the go runtime will do a conversion automatically.</p> <p>&#34;Interface in Go are pure &#39;behaviour&#39;...&#34; dixit D.Cheney. Here elem will behave as a &#39;string&#39;... :)</p> <p>See the 2 articles to read about interfaces: <a href="http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go" rel="nofollow">http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go</a> and for detail about the implementation: <a href="http://research.swtch.com/interfaces" rel="nofollow">http://research.swtch.com/interfaces</a></p></pre>

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

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