<p>Is there a more elegant way to do this (i.e. without a loop)?</p>
<pre><code>type Symbol string
type Production []Symbol
func (p Production) String() string {
strs := make([]string, len(p))
for i, symbol := range p {
strs[i] = string(symbol)
}
return strings.Join(strs, " ")
}
</code></pre>
<p><a href="https://play.golang.org/p/c3sRk0cl7b" rel="nofollow">https://play.golang.org/p/c3sRk0cl7b</a></p>
<hr/>**评论:**<br/><br/>djherbis: <pre><p>Explicit conversion (as you have done) is the proper way in Go to convert between these two types.</p>
<p>That being said, since Symbol and string have the same underlying type, by coincidence []Symbol and []string will have the same layout in memory so <a href="https://play.golang.org/p/GwS5mitmbw" rel="nofollow">there is a way, but you shouldn't do it</a>.</p>
<p>This is not a safe transformation though, it's not guaranteed to work by the language and could be changed to break at any point. It's taking advantage of an implementation detail that you shouldn't. But it's still neat to know how it works.</p>
<p>edit: also note that mutations done to elements in the slice will affect both slices as well, since they are backed by the same memory.</p></pre>adamcolton: <pre><p>Thanks! That's a neat trick. I'll follow your advice and avoid it. But it is interesting to see.</p></pre>dchapes: <pre><p>tl;dr: Unless this is proven bottleneck effecting performance just leave the simple obvious implementation in place.</p>
<p>If you <em>really</em> care about a few hundreds of nanoseconds and one extra allocation, you can get the same performance as the unsafe implementation in both time and memory with tiny amount of copying from <code>strings.Join</code>:</p>
<pre><code>func (p Production) String() string {
if len(p) == 0 {
return ""
}
n := len(p) - 1
for _, sym := range p {
n += len(sym)
}
b := make([]byte, n)
bp := copy(b, p[0])
for _, s := range p[1:] {
bp += copy(b[bp:], " ")
bp += copy(b[bp:], s)
}
return string(b)
}
</code></pre>
<p><a href="https://play.golang.org/p/del0XNkfJS" rel="nofollow">https://play.golang.org/p/del0XNkfJS</a></p>
<p>On the sample input from the above link this gives me:</p>
<pre><code>original: 1000000 1899 ns/op 5376 B/op 3 allocs/op
using manual Join: 1000000 1384 ns/op 3584 B/op 2 allocs/op
using unsafe: 1000000 1396 ns/op 3584 B/op 2 allocs/op
</code></pre></pre>ar1819: <pre><p>Because of the Go memory model this, while being unsafe, will work in all cases, because distinction between []Symbol and []string is more about type than its about memory. The newly created type based on another will have the same layout as the predecessor.</p>
<p>The difference exists, however, and it's fully used by interfaces, which use type information, known at compile time, to store type info field, which is then used during method call resolution. </p>
<p>Keep in mind that using unsafe implies that you are familiar with memory model of Go and how different types are represented on different architectures. Don't use it unless it's absolutely necessary, because unsafe import is a instant warning flag for any security oriented developer, and such code is usually heavily audited. </p>
<p>To give you an example why you should not do it - on x86 systems unsafe cast from []rune to []int is safe while on amd64 it will lead to segfault at best and to memory curruption at worst.</p></pre>Uncaffeinated: <pre><blockquote>
<p>To give you an example why you should not do it - on x86 systems unsafe cast from []rune to []int is safe while on amd64 it will lead to segfault at best and to memory curruption at worst.</p>
</blockquote>
<p>Out of curiosity, why is this? Different alignment requirements?</p></pre>ar1819: <pre><p>rune is 32 bits on every platform, while int is platform dependant so it's either 32 or 64. Or maybe even 128 in distant future on exotic platforms. </p></pre>tmornini: <pre><p>Not near a keyboard, and. It 100% certain, but you may be able to convert p to slice of string:</p>
<p>strings := []string(p)</p>
<p>Then join that...</p></pre>adamcolton: <pre><p>Tried, doesn't work. <a href="https://play.golang.org/p/Ozf9yGmIZD" rel="nofollow">https://play.golang.org/p/Ozf9yGmIZD</a></p></pre>tmornini: <pre><p>Too bad!</p>
<p>Thanks!</p></pre>peterbourgon: <pre><p>Nope.</p></pre>gac_web: <pre><p>There is no casting in Go, it's either type conversion or type assertion. There is a significant difference as Go types aren't covariant.</p></pre>adamcolton: <pre><p>Ok, it's a conversion not a cast. It's not an assertion because that only applies to interfaces, which we're not dealing with here. But is there a way to do it without the loop?</p></pre>gac_web: <pre><p>My point here is that there is absolutely nothing you can do in that case if you want to stay compile time type safe. You have to write a for loop.</p></pre>Hactually: <pre><p>This should now work on go 1.8. playground is still at 1.7 tho</p></pre>adamcolton: <pre><p>I tried this: <a href="https://play.golang.org/p/Ozf9yGmIZD" rel="nofollow">https://play.golang.org/p/Ozf9yGmIZD</a> on 1.8 and it did not work. Gives the same error:
cannot convert p (type Production) to type []string</p>
<p>I tried a few other variations and nothing without a loop (or unsafe) worked.</p></pre>dchapes: <pre><p>I can only image you're confusing this with the <a href="https://golang.org/doc/go1.8#language" rel="nofollow">go1.8 change to struct conversions</a>, which is a completely different thing. There have been (and AFAIK are no plans) to change how assignment of types with incompatibly layouts work (i.e. it doesn't).</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传