Passing slice of values as slice of interfaces

blov · · 687 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;m playing with Go, working on a little pet project. The language is pretty intuitive so far, but I&#39;m not grasping some of the limitations imposed on interfaces.</p> <p>I&#39;ve got something that looks like this:</p> <pre><code>type a interface { method() int } func (b mytype) method() int { ... } func somefunc(s []a) { ... } ... var b []mytype ... somefunc(b) </code></pre> <p>When I try to pass a <code>[]mytype</code> into somefunc(), I get this error</p> <pre><code>cannot use b (type []mytype) as type []a in argument to somefunc </code></pre> <p>Is there a reason this doesn&#39;t work? Is there any way around this without casting each element? Because then, I wouldn&#39;t really be getting the full benefit of using interfaces in the first place.</p> <hr/>**评论:**<br/><br/>HectorJ: <pre><p><a href="http://stackoverflow.com/a/12754757/1685538" rel="nofollow">http://stackoverflow.com/a/12754757/1685538</a></p></pre>borring: <pre><p>So the reason it&#39;s not the default behavior is because it&#39;s an expensive operation? I ended up needing to write one more for loop because of it. But it isn&#39;t <em>that</em> much more work.</p></pre>SteveMcQwark: <pre><p>It&#39;s not that it&#39;s more work to write the code, but that it&#39;s more work to execute it. Normally, copying a slice is a really cheap constant time and memory operation. Converting it to an interface slice makes it a linear time and memory operation. Considering that a lot of people try to do this before they&#39;re even aware of the representation issue, it makes sense to make this change in behaviour explicit rather than hiding it behind misleading syntax.</p> <p>Edit: Ignore this if by &#34;but it isn&#39;t <em>that</em> much more work&#34; you meant that you&#39;re okay with writing the loop, and not that you think the compiler should just do it.</p></pre>borring: <pre><p>I meant it as in writing one tiny for-loop isn&#39;t really that much more work. I needed to write one more loop than I thought I needed is all.</p> <p>But on the topic of hiding expensive operations, what about the <code>append()</code> builtin? IFAIK, when the slice being appended to is too small, it&#39;ll copy the contents to a new array but use the same array otherwise. Isn&#39;t copying to a new array also O(n)?</p></pre>SteveMcQwark: <pre><p>It&#39;s amortized O(1), because it allocates enough room to keep appending elements without having to reallocate. On top of that, append is a function, so you know you&#39;re opting in to its behaviour.</p></pre>gngl: <pre><p>If the change were implicit, it would also cause a confusing behavior where sometimes, a handed-off slice could be modified by a callee (because its backing array would be an actually physically shared piece of memory) while at other times, it wouldn&#39;t work for some reason mysterious to the beginning Go-er.</p></pre>SteveMcQwark: <pre><p>Go (unfortunately) already has a similar issue with variadic functions:</p> <p><a href="http://play.golang.org/p/cek7O86blj" rel="nofollow">http://play.golang.org/p/cek7O86blj</a></p> <p>Sometimes you&#39;re changing your arguments. Sometimes you aren&#39;t. Ideally, variadic arguments wouldn&#39;t be mutable, so this wouldn&#39;t be visible, but Go doesn&#39;t provide that concept, so it&#39;s a rough edge people have to be aware of.</p></pre>gngl: <pre><p>Huh. Interesting... I guess I&#39;m a beginner. :)</p></pre>gngl: <pre><p>If the slice is writable (and it always is in Go, if I&#39;m not mistaken), you can&#39;t pass it to somefunc purely on the basis of general type theory, because somefunc might attempt to write into the slice a value of the interface type <strong>a</strong> that isn&#39;t actually <strong>mytype</strong> (there could be <em>other</em> implementations of type <strong>a</strong> besides <strong>mytype</strong>) and you&#39;d either get immediately a type error at runtime, or later an outright crash, depending on how that assignment would be handled. This is conceptually independent of the way of how the values are represented.</p> <p>Furthermore, when it comes to representations, interfaces don&#39;t work the same way in Go as they do in other languages since Go values don&#39;t have headers and the two types (a value and an interface reference to that value) are not of the same size, interface values contain explicit type information. And even if they happened to be the same size (16B on 64 bits?), they&#39;d have different physical bit patterns anyway so you&#39;d have to physically convert the data anyway.</p> <p>In contrast to this, in languages like Java, an array of objects of some class can be treated as an array of objects implementing an interface (that is implemented by said class) simply by pretending that it is like that, since it&#39;s the same physical pointers to &#34;headered&#34; objects in both cases. </p> <p>On the other hand, in exchange for this (occasional? or even rare?) hassle, you get internal pointers in Go as a compensation, and in most cases, you&#39;ll generally probably use much less memory for comparable amount of useful data in Go - also better cache use. Totally worth it in many people&#39;s book.</p> <p>Of course, Java also got this wrong <em>by actually allowing this kind of array casting.</em> They&#39;ve paid for it dearly by risking type errors at run time, as I wrote above.</p> <p>Complex interplays like this (subtyping-induced covariance and contravariance problems) are why in general, generics and OOP-style subtyping are an explosive mixture (slices <em>are</em> generics in Go - they&#39;re just privileged in the sense that you can&#39;t create your own generic types). As is a number of other features, which is one of the reasons why Go tries to remain as simple as possible.</p></pre>calebdoxsey: <pre><p>Go doesn&#39;t support covariance on slices. Two ways to work around it:</p> <ul> <li>Convert your <code>[]mytype</code> to a <code>[]a</code></li> <li><p>Don&#39;t use slices, take an interface instead and implement that interface: <a href="https://play.golang.org/p/q6Vl-YbKVi" rel="nofollow">https://play.golang.org/p/q6Vl-YbKVi</a></p> <pre><code>package main import &#34;fmt&#34; type A interface { method() int } type B int func (b B) method() int { return int(b) } type Enumerable interface { Each(handler func(A)) } type AList []A func (as AList) Each(handler func(A)) { for _, a := range as { handler(a) } } type BList []B func (bs BList) Each(handler func(A)) { for _, b := range bs { handler(b) } } func somefunc(s Enumerable) { s.Each(func(a A) { fmt.Println(a.method()) }) } func main() { as := []A{B(1), B(2)} somefunc(AList(as)) bs := []B{B(1), B(2)} somefunc(BList(bs)) } </code></pre></li> </ul> <p>That&#39;s a fair bit of extra code... I wouldn&#39;t do that for this example, but it&#39;s a good strategy for when you really do need it. (This is how the sort package works)</p></pre>borring: <pre><p>Wow, that was a pretty elegant way to do it. I had no idea that sort was part of the Go stdlib. I guess I should have searched for it first. Sorting was <em>exactly</em> what I was trying to do. I already implemented it though.</p> <p>I&#39;ll count it as a programming exercise and delete that my chunk of code in favor of the stdlib</p></pre>

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

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