Elegant Type Casting?

polaris · · 406 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<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, &#34; &#34;) } </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&#39;t do it</a>.</p> <p>This is not a safe transformation though, it&#39;s not guaranteed to work by the language and could be changed to break at any point. It&#39;s taking advantage of an implementation detail that you shouldn&#39;t. But it&#39;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&#39;s a neat trick. I&#39;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 &#34;&#34; } 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:], &#34; &#34;) 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&#39;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&#39;t use it unless it&#39;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&#39;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&#39;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&#39;s either type conversion or type assertion. There is a significant difference as Go types aren&#39;t covariant.</p></pre>adamcolton: <pre><p>Ok, it&#39;s a conversion not a cast. It&#39;s not an assertion because that only applies to interfaces, which we&#39;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&#39;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&#39;t).</p></pre>

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

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