<p><a href="https://play.golang.org/p/UZZJF0c1nK">https://play.golang.org/p/UZZJF0c1nK</a></p>
<p>This is the best I could come up with. I wonder if this is the right way to do it.</p>
<hr/>**评论:**<br/><br/>broady: <pre><p>Yes, that's right.</p></pre>iopred: <pre><p>I would allocate an array of the same size as the original, would avoid needing to append constantly.</p></pre>bogaczio: <pre><p>While I'm sure you already know this, OP might not be as familiar with Go, so let's not forget the <a href="https://play.golang.org/p/63asb0QhQT" rel="nofollow">difference between make([]T, len) and make([]T, len, cap)</a>. </p></pre>nesigma: <pre><p>Is there any reason to use this style?</p>
<pre><code>foo = make([]string, 0, 5)
foo = append(foo, "my string")
</code></pre>
<p>Usually I seem to be going for </p>
<pre><code>var foo []string
foo = append(foo, "my string")
</code></pre></pre>driusan: <pre><p>The former is more efficient.</p>
<p>Let's say you're in a loop: The latter starts out backed by an array of size 0. The first append allocates a new array to back it and stores "my string" there, the next one realizes that there's no space to append, so allocates a new array, moves "my string" into it, and appends the second "my string", garbage collecting the first array, the next one realizes there's no space to append, so allocates a new bigger array and moves the two "my strings" into it , appends the third, and garbage collects the second array, etc.</p>
<p>The former starts with a slice backed by an array of size 5. Even though the length of the slice is size 0, it doesn't need to reallocate until it reaches the capacity of 5. </p>
<p>I think the Go implementation doubles the capacity each time you append to something that's full instead of just allocating size+1, but that's an implementation detail. The point is, there's a lot of overhead if you start with a nil slice and keep appending. If you know the size you're going to end up with (or even just know the maximum potential size), you can save a lot of work by just directly allocating a capacity of that size.</p></pre>nesigma: <pre><p>Thanks, I wasn't aware of that. I was mostly using the second style because it seemed easier. Two questions:</p>
<p>So if I use <code>foo = make([]string, 0, 5)</code> before, does that reduce the overhead of append? </p>
<p>Also if I end up appending 3 elements instead of 5 and I do len(foo) will it be 3 or 5?</p></pre>bogaczio: <pre><p>If you use tha <code>cap</code> argument (that third one), and you append three times, then it will be <code>len(foo) = 3</code>. You can look at the playground link I posted above for an example of that. </p>
<p>Instantiating it with <code>make([]string, 0, 5)</code> means that <code>append</code> won't trigger a memory allocation until you reach that capacity. Otherwise, you would trigger a memory allocation at the first append, then the second (since the underlying array is doubled each time), and then the fourth. In practice, you probably won't notice these allocations unless you're really trying to push the envelope, or you're doing a lot of appending to smallish slices, since the allocation cost is amortized the larger a slice you're using.</p></pre>driusan: <pre><p>make([]string, 0, 5) creates a slice of length zero, backed by an array of size 5. That is, it's essentially the same as:</p>
<pre><code>var bar [5]string
foo := bar[0:0]
</code></pre>
<p>with bar hidden in the background. len(foo) is 0 (the second argument to make). len(bar) is the cap of the slice foo (the third argument to make). If you append to foo and len(foo) is already 5, then Go needs to allocate a new bar, and point foo it, instead of the original.</p></pre>epiris: <pre><p>I second this only if you have a guarantee that all members of T are of the same type. If it's more like a filter operation with an unknown count then append is likely best.</p></pre>SSoreil: <pre><p>Just a thought, but seeing as how it is often pretty easy for the user to spot this optimization, couldn't the compiler also in trivial cases make this optimization itself?
Looking at playground code it seems for this case it probably wouldn't work though.</p></pre>nesigma: <pre><blockquote>
<p>would avoid needing to append constantly.</p>
</blockquote>
<p>In general, is append considered bad and why? I seem to be using it very often in my code so if it is not good please let me know.</p></pre>nathankerr: <pre><p>Did you intend to ignore everything from uglyFunc that is not *Foo? If so, leave a comment to indicate it is intentional.</p>
<p>I don't like that non *Foo items are ignored. Since ulgyFunc returns []interface{}, the code needs to deal with that. Letting the type assertion panic is a valid way of dealing with this, and will even shorten your code.</p></pre>nesigma: <pre><blockquote>
<p>Did you intend to ignore everything from uglyFunc that is not *Foo?</p>
</blockquote>
<p>My original code was returning an error in case the thing was not *Foo. But then I figured, if the thing is really not *Foo then there really nothing I can do so I might just well ignore it but maybe returning an error is better after all.</p>
<blockquote>
<p>Letting the type assertion panic is a valid way of dealing with this, and will even shorten your code.</p>
</blockquote>
<p>Can you show me how? In my original code it was <code>if !ok { return the error }</code>. I am not familiar with using panic.</p></pre>Shammyhealz: <pre><pre><code>for _, u := range ugly {
foo = append(foo, u.(*Foo))
}
return foo
</code></pre>
<p>Like that. You don't actually have to force it to panic, Go does it automatically if you try to do do a single value type assertion (i.e. you don't provide an error variable to the type assertion). If, for whatever reason, one of the variables in ugly isn't a *Foo, your program will panic and crash. If you don't want that to happen and have a way of dealing with, you can use a recover() in whatever calls SafeFunc.</p>
<p>There's a good guide to defer, panic and recover at <a href="https://blog.golang.org/defer-panic-and-recover" rel="nofollow">https://blog.golang.org/defer-panic-and-recover</a></p>
<p>I agree with <a href="/u/nathankerr" rel="nofollow">/u/nathankerr</a> that it's probably bad that you're just going to ignore non *Foo items in that slice. If you think that it should never ever happen that a non *Foo item is in that slice, then use something like the code I put up above. You could also have SafeFunc return an error along with your slice of *Foo, but I don't think that's what you want. Based on the fact that you're only converting to a single type, I think you probably want to crash out the program if you get something like a string or an int in that slice.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传