<p>I know that in many cases interfaces can give the same benefits as generics. I also see how doing it with interfaces can be much cleaner than similar ways in other languages but there are some cases where I don't see any way to make something truly type safe. Take the built in Linked List implementation for example. It has a data type: </p>
<pre><code> type Element struct {
Value interface{}
}
</code></pre>
<p>that is not type safe. For example:</p>
<pre><code> package main
import(
"container/list"
"fmt"
)
func main() {
l := list.New();
l.PushBack("hello world");
l.PushBack(1);
l.PushBack(2);
l.PushBack(12.987);
l.PushBack("This is not type safe...");
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value);
}
}
//Output:
//hello world
//1
//2
//12.987
//This is not type safe...
</code></pre>
<p>Is golang simply just not truly type safe? Or is there something that I am missing?</p>
<hr/>**评论:**<br/><br/>weirdasianfaces: <pre><p>It's not that using <code>interface{}</code> is "not type safe". It's basically the equivalent of using <code>void*</code> in C to get around not knowing what a type may be. You use type assertions to assert that something is indeed of the type you are looking for:</p>
<pre><code>if val, ok := e.Value.(int); ok {
fmt.Println("This is an int", int)
}
</code></pre>
<p>In order to actually perform any operations on the value (that don't involve passing it to something else that takes an <code>interface{}</code>) you need to use assertions.</p></pre>TheMerovius: <pre><blockquote>
<p>It's basically the equivalent of using <code>void*</code> in C</p>
</blockquote>
<p>It's fundamentally different from <code>void*</code>, in that <code>interface{}</code> is actually type safe, while <code>void*</code> isn't :) <code>interface{}</code> carries the type of the contained value, which makes it impossible to wrongly interpret it as a different one, which is (runtime) type safety.</p></pre>Addr0x11: <pre><p>Yea and please mention the overhead added by it.</p></pre>ston1th: <pre><p>Just a little hint.</p>
<p>It's <code>fmt.Println("This is an int", val)</code> instead of <code>fmt.Println("This is an int", int)</code>.</p></pre>ihsw: <pre><blockquote>
<p>you need to use assertions</p>
</blockquote>
<p>This is a whole different can of worms than generics, and frankly I think assertions have no place <em>at all</em> in Go. Even less than generics.</p>
<p>Other than unit tests, of course.</p>
<p>Maybe it's because I've never written a line of C/C++ code and I simply cannot grok using assertions in that manner, but, based on my limited understanding of them, they make my skin crawl. Very un-Go and the designers eschewed including them for the same reason exceptions are nowhere to be found.</p>
<p>Please correct me if I'm wrong.</p>
<p>EDIT: <a href="http://stackoverflow.com/questions/1571340/what-is-the-assert-function" rel="nofollow">C++-style assertions</a>, not type assertions.</p></pre>djherbis: <pre><p>I think un-Go is harsh, it's a whole section in Effective Go: </p>
<p><a href="https://golang.org/doc/effective_go.html#interface_conversions">https://golang.org/doc/effective_go.html#interface_conversions</a></p>
<p>Personally, I'm a fan of either using code-gen or wrapper types to get type specific structures. However there's really nothing wrong with using interface{} and type assertions (or even type switches) when appropriate.</p></pre>ihsw: <pre><p>Oh, type assertions. I was thinking of <a href="http://stackoverflow.com/questions/1571340/what-is-the-assert-function" rel="nofollow">these assertions</a>.</p>
<blockquote>
<p>assert will terminate the program (usually with a message quoting the assert statement) if its argument turns out to be false. it's commonly used during debugging to make the program fail more obviously if an unexpected condition occurs.</p>
</blockquote>
<p>That said, <code>interface{}</code> with type assertions/switches are <em>gross</em> IMO. Even though explicit unboxing is supported and sometimes encouraged, I can't help but cringe a little when I see <code>interface{}</code> in func definitions.</p></pre>postman_: <pre><p>That's what you pay for the so-called "simplicity" of Go.</p></pre>ihsw: <pre><p>Generics are a bit too magic-y for me, even though I work with them on a regular basis in other stacks.</p>
<p>Frankly I hope they stay in limbo. What's next, operator and func overloading?</p></pre>thockin: <pre><p>I love this argument. I don't understand this (powerful) concept (which most modern languages have) so I hope Go doesn't get something like it (even if it could make all sorts of code simpler).</p></pre>ihsw: <pre><p>I do understand it and I still think it has no place in Go.</p></pre>postman_: <pre><blockquote>
<p>Generics are a bit too magic-y for me</p>
</blockquote>
<p>Then why won't you read more about them?</p>
<blockquote>
<p>What's next, operator and func overloading?</p>
</blockquote>
<p>I'd be happy to see this in Go.</p></pre>weirdasianfaces: <pre><p>Assertions are needed for very specific tasks. If you remove type assertions from the language in its current state then doing "generic" things like <code>fmt.Println</code> would not be possible without assertions (while avoiding reflection I guess). See <a href="https://golang.org/src/fmt/print.go#L763">line 763</a>.</p></pre>ihsw: <pre><p>Oh, type assertions. I was thinking of <a href="http://stackoverflow.com/questions/1571340/what-is-the-assert-function" rel="nofollow">these assertions</a>.</p>
<blockquote>
<p>assert will terminate the program (usually with a message quoting the assert statement) if its argument turns out to be false. it's commonly used during debugging to make the program fail more obviously if an unexpected condition occurs.</p>
</blockquote></pre>dilap: <pre><p>Yep, there's no really no better way to do it in Go.</p>
<p>Rather than thinking type safe or not, it's helpful to think of it as, "how many static safety guarantees does my language give me". At one extreme you have languages like say Python or Ruby that basically guarantee you nothing; at the other extreme, stuff like (I dunno) Agda or Haskell that give you a ton of guarantees.</p>
<p>Go sits somewhere in the middle. It includes just enough to catch a lot of errors but still be really simple.</p></pre>deslittle: <pre><p>The standard library implementation of a linked list casts any type you pass into PushBack() as the interface{} type.
You are correct in that you can't do much with this type when it comes back out from Front() without using reflection, however you can implement your own Linked List that accepts a more useful interface type without using reflection.</p>
<p>In the standard library list.go, simply define a custom interface such as:</p>
<pre><code>type Printer interface{
Print()
}
</code></pre>
<p>and then use this type in the Element struct:</p>
<pre><code>type Element struct{
Value Printer
}
</code></pre>
<p>You can now perform actions on the returned element:</p>
<pre><code>for e := l.Front(); e != nil; e = e.Next() {
e.Value.Print();
}
</code></pre>
<p>as e.Value is now of type Printer. </p>
<p>You can now pass any type into PushBack() as long as that type implements Print(), otherwise the compiler will complain.</p>
<p>As long as you are implementing the interface in your own code you know how the elements are supposed to behave when they are returned from the list. With the standard library, they have no idea how the returned elements will be used by the end user so they use the empty interface, and leave it to end user to pull out the original type using reflection.</p></pre>TheMerovius: <pre><blockquote>
<p>without using reflection</p>
</blockquote>
<p>No. <a href="https://godoc.org/reflect" rel="nofollow">This is reflection</a>. What you are looking for is <a href="https://golang.org/doc/effective_go.html#interface_conversions" rel="nofollow">type assertions</a>. (sorry, people using "reflection" incorrectly is a pet peeve of mine :) )</p></pre>deslittle: <pre><p>Yes you are correct, I did indeed mean type assertion. I think my point still stands though, you most definitely can implement a 'type safe' linked list in Go using interfaces. </p></pre>earthboundkid: <pre><p>There are a couple of projects to do pre-compilation macros in Go if you really need generic data types, but usually you don't. </p></pre>TheMerovius: <pre><p>First a nit: It's simply "go", not "golang" :) Now to your post:</p>
<p>The go gods will tell you, that "type safe" is the wrong term to use here. <code>interface{}</code> is completely type safe. Every value has a type and you can't interpret it as a different type (except in very narrow circumstances) even by accident. That's type safety.</p>
<p>What you are looking for is "statically type checked". And even there, people might tell you, that the inbuilt version of a linked list is indeed statically type checked. It just uses a different kind of type (namely an interface type).</p>
<p>So the question you are looking for is "are there parameterized types in go", i.e. you want one type, that can contain arbitrary types as an element, but gives the static guarantee that you use every value always with the same type. And phrased like that, the answer is famously "no". Because that's what generics (most often) mean. And go doesn't have these.</p>
<p>However, this shouldn't really deter you. It turns out, that this isn't really a problem in practice. If you <em>know</em> a list should only contain a certain type, you <em>won't</em> pass in other types and you can do simple type assertions (even without <code>, ok</code> idiom) to unpack it. Yes, you don't get the compiler guarantee. But even without this guarantee, it still never (up to an error of measurement) happen. Promise :)</p></pre>leovailati: <pre><p>Go does have generics, but only the built-in ones: <code>map[T]U</code>, <code>chan T</code> and <code>[]T</code>. I personally don't like how the designers of the language decided on leave user-defined generics behind, feels to me like an oversimplification, but that's only my opinion.</p></pre>jmank88: <pre><p>Golang is golang. There are more and less safe ways to do things. You can type check your interface values if you want to be safe. Or you can write type specific implementations or wrappers if you want safety and/or speed.</p></pre>egonelbre: <pre><p>See <a href="https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/edit#" rel="nofollow">summary of go generics</a>. Also linked list is a bad example, because it shouldn't be used in the first place (most of the time).</p></pre>postman_: <pre><blockquote>
<p>Or is there something that I am missing?</p>
</blockquote>
<p>Yes, the meaning of the term "type safety".</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传