Question regarding embedding an interface in a struct

agolangf · · 557 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>So I&#39;m working through learning Go, and I&#39;ve come across something that got me curious, especially relating to Go&#39;s emphasis on explicitness, type checking, etc.</p> <p>Given a small program that does nothing of any real consequence:</p> <pre><code>package main import ( &#34;fmt&#34; ) type bar struct { } type FooInterface interface { GetBar() bar SetBar(bar) } type Baz struct { FooInterface } func main() { var tribble Baz = Baz{} var trubble FooInterface = &amp;tribble z := tribble.GetBar() x := trubble.GetBar() fmt.Println(z) fmt.Println(x) } </code></pre> <p>So now we have a struct type that embeds Interface FooInterface, that struct does not implement any of the methods of FooInterface.</p> <p>Running this code causes</p> <pre><code>panic: runtime error: invalid memory address or nil pointer dereference </code></pre> <p>I understand why the code doesn&#39;t work, but given how strict Go is, <em>why is this even allowed to compile?</em></p> <p>Go has implicit implementation of Interfaces which is terrific and all, but why is embedded/explicit implementation as wonky as this seems to be?</p> <p>I would expect embedded implementation to behave in such a way that a struct <em>must</em> implement the entire methodset of the implemented Interface, even if none of those methods are called, but it seems to do the exact opposite of this.</p> <p>It seems to me that it&#39;s performing the same action as when a user embeds a struct for subclassing/inheritance in that it&#39;s making those subclassed/inherited methods available.</p> <p>Is there any reason for this, or is this just functionality that should be added in the future?</p> <hr/>**评论:**<br/><br/>DeedleFake: <pre><p>An anonymous field in a struct is no different from any other field except for the method/field promotion. In other words, there isn&#39;t much difference between embedding a struct or embedding an interface. The big difference is that you can&#39;t call methods on a non-nil interface. For example:</p> <pre><code>type Example struct { io.Reader } func main() { file, _ := os.Open(&#34;example.txt&#34;) defer file.Close() e := &amp;amp;Example{file} buf := make([]byte, 1024) e.Read(buf) // Exactly the same as e.Reader.Read(buf) } </code></pre> <p>For more information, see <a href="https://golang.org/ref/spec#Struct_types" rel="nofollow">the spec&#39;s section on struct types</a>.</p></pre>TheMerovius: <pre><blockquote> <p>I understand why the code doesn&#39;t work, but given how strict Go is, why is this even allowed to compile?</p> </blockquote> <p>Because there is - in general - absolutely no way to prevent this at compile time. For that, the compiler would need to know what value is saved in that embedded interface and that is in general equivalent to the halting problem, which isn&#39;t solvable in general. So go falls back to a runtime-check, which is the sensible thing to do.</p> <blockquote> <p>I understand why the code doesn&#39;t work</p> </blockquote> <p>Do you really? No offense, but that is not really my impression. For example:</p> <blockquote> <p>I would expect embedded implementation to behave in such a way that a struct must implement the entire methodset of the implemented Interface, even if none of those methods are called, but it seems to do the exact opposite of this.</p> </blockquote> <p>I don&#39;t know what you mean here. Baz <em>does</em> implement the interface, it has methods <code>GetBar() bar</code> and <code>SetBar(bar)</code> and the implementation of both is to call the respective method on the embedded interface. The code panics because that interface is nil, but that is something that the compiler can not know in general (see above). So this <em>must</em> be a runtime check. You then assign a *Baz to a FooInterface and again, *Baz implements this interface due to how method sets work.</p> <blockquote> <p>It seems to me that it&#39;s performing the same action as when a user embeds a struct for subclassing/inheritance in that it&#39;s making those subclassed/inherited methods available.</p> </blockquote> <p>Yes, that is exactly what is happening - if you look at the <a href="https://golang.org/ref/spec#Struct_types" rel="nofollow">spec</a>, it talks about embedding a type and &#34;promoting&#34; fields or methods to the struct that embeds it. This is what&#39;s happening here, but the promoted method is on a nil-interface, so it panic&#39;s.</p> <p>So your whole problem boils down to &#34;why does <a href="http://play.golang.org/p/9UCpXL0k2P" rel="nofollow">this</a> compile?&#34; and the answer to that is given above.</p> <blockquote> <p>Is there any reason for this, or is this just functionality that should be added in the future?</p> </blockquote> <p>I would be really curious what kind of &#34;functionality&#34; you are talking about. So far you haven&#39;t really said <em>why</em> this shouldn&#39;t compile and how the compiler should figure that out.</p></pre>Ranger_X: <pre><p>My understanding of interfaces is that they are specifications that require an object to define methods that have a certain method signature. The interface doesn&#39;t care how you implement them, it only cares that these methods are defined so on any struct that implements the Interface, the interface methodset can be called on it. The Interface should not define these methods itself.</p> <p>The &#34;functionality&#34; I&#39;m talking about is that any struct that implements an Interface to have the methods required by said Interface to be non-nil at compile time (which you mentioned isn&#39;t currently possible). If it&#39;s uncheckable at compile time, then it&#39;s uncheckable at compile time and that&#39;s that I suppose.</p> <p>If Interfaces in Go are meant to define default behavior for embedding objects, then I was operating on a bad assumption and I can see where I was in error. Is this the case?</p></pre>TheMerovius: <pre><blockquote> <p>My understanding of interfaces is that they are specifications that require an object to define methods that have a certain method signature. The interface doesn&#39;t care how you implement them, it only cares that these methods are defined so on any struct that implements the Interface, the interface methodset can be called on it. The Interface should not define these methods itself.</p> </blockquote> <p>That is correct. It is also a tautology in go, as you can&#39;t define methods on interface types (well… an interface type has a method set, but that&#39;s just the methods that a type needs to have to satisfy this interface).</p> <p>You should probably, to start understanding what is happening, differentiate between &#34;interface types&#34; and &#34;interface values&#34;. </p> <blockquote> <p>The &#34;functionality&#34; I&#39;m talking about is that any struct that implements an Interface to have the methods required by said Interface to be non-nil at compile time (which you mentioned isn&#39;t currently possible)</p> </blockquote> <p>It&#39;s not <em>currently</em> impossible, it is <em>in principle</em> impossible.</p> <blockquote> <p>If Interfaces in Go are meant to define default behavior for embedding objects</p> </blockquote> <p>You keep rephrasing stuff in weird ways that I don&#39;t understand. No, interfaces are definitely <em>not</em> meant to &#34;define default behavior&#34;. They are meant to define a method set. They are meant to basically duck-type, to say &#34;anything that has these methods can go here&#34;. A variable/field of interface type can hold a value of any type that implements the methods </p> <p>Type Embedding - an orthogonal concept - is meant to promote fields or methods of a type and make the embedding type &#34;inherit&#34; (that&#39;s the wrong word, because it isn&#39;t really inheriting anything, but oh well) these.</p> <p>Both have many uses and their composition has even more (because that&#39;s pretty much what orthogonal means). One example is, that you can make a new type implement a given interface by embedding an existing type that already implements this interface. And a special case of that example is, if the embedding type is an interface type itself (this is the special case of a special case that we are currently talking about).</p> <p>This special case of that example now has a couple of interesting usecases, none of which can be described as &#34;defining default behavior&#34;.</p> <p>For example, the aforementioned sort-case: Selectively overwriting methods of a given interface. Note, that &#34;defining default behavior&#34; is as far as you can possibly get from describing what you are doing there. You are not defining default behavior, but you are extending user-provided behavior - the wrapped sort.Interface - to do something different.</p> <p>Another use case, not mentioned so far, is separating the required from the offered interface. In C++ you might do that by defining a set of purely virtual methods - the required interface that a consumer of your API needs to implement in a derived type - and then add a set of non-virtual functions that call these virtual functions - the offered interface, that is defined in terms of the implementation of the required one. A user can then inherit from that class and provide an implementation of the required interface in the form of overriding the purely virtual functions to get the functionality of the offered interface. In go, the way to do that is by defining an interface type for the required interface, embedding that interface type into a struct and then defining additional methods that call into these interface-methods - again, the offered interface. A user of your package can then implement the required interface in their own type and embed a value of that type in your provided struct to get the offered interface. This is a very good example of how composition can be used to replace inheritance.</p> <p>In any case, I have a huge problem with your phrasing because a) it is completely mischaracterizing what is actually happening and b) you are reducing &#34;the meaning of interfaces&#34; to how you are misunderstanding the special case of a special case of their use.</p> <p>I am more and more certain that your real criticism is, that there is a zero value of an interface type (namely nil) which still has all the methods of that interface type. But that is a corollary of a) interfaces being first-level types, b) memory being zero-initialized (so every first-level type needs a zero-value) and c) the halting problem as stated above (as the compiler can&#39;t know if at any given point an interface variable contains the zero value or some other value). a) and b) are individually <em>very</em> much desirable properties and c) is a logical consequence of what a program is. So I find &#34;there might be a runtime error when I use an interface value uninitialized&#34; to be an extremely low price to pay (especially as there are, for example, also runtime-errors if you use a pointer-value uninitialized).</p></pre>Ranger_X: <pre><p>First off, I want to thank you for writing such in-depth responses, and I apologize for not phrasing my questions/responses in usual conventions (I&#39;m a junior dev learning Go for fun and expanding my learning).</p> <blockquote> <p>It&#39;s not <em>currently</em> impossible, it is <em>in principle</em> impossible.</p> </blockquote> <p>This probably is the answer that answers my question, but it&#39;s not the answer I was hoping for heh. Since it&#39;s impossible to check at compile time that the methodset of an embedded interface is actually defined in a non-nil way, what I figured Go would do is not able to be done.</p> <p>Thanks for your help in getting me to understand why Go doesn&#39;t check the methodset of an embedded interface actually being defined/fulfilled</p></pre>barsonme: <pre><p>This SO question should (mostly) answer your question: <a href="http://stackoverflow.com/questions/24537443/meaning-of-a-struct-with-embedded-anonymous-interface" rel="nofollow">http://stackoverflow.com/questions/24537443/meaning-of-a-struct-with-embedded-anonymous-interface</a></p> <p>I&#39;d try to go in more depth but I&#39;m on mobile, sorry.</p></pre>Ranger_X: <pre><p>I saw that earlier today; so is the point of explicitly embedding an interface into a struct so you don&#39;t have to define the methods you aren&#39;t going to use? That seems out of character with all the other stuff I&#39;ve been learning about Go</p></pre>TheMerovius: <pre><p>No, the point of making interface embedding work is that you can overwrite specific methods of it. Did you understand the <a href="http://play.golang.org/p/0a5e8gh4A8" rel="nofollow">sort example</a> and how reverse works? And do you understand that, without interface embedding you would need to do <a href="http://play.golang.org/p/MIVxhjbksj" rel="nofollow">this</a>? And why, thus, it is useful to embed interfaces?</p></pre>Ranger_X: <pre><p>I can understand the reasons behind Interface embedding, but I&#39;m under the impression that Interfaces shouldn&#39;t define a method&#39;s default behavior by itself, but should require the embedding structs to define them. Is this not the case in Go?</p></pre>SportingSnow21: <pre><p>You&#39;re confusing embedded interfaces with the keyword &#34;implements&#34; in other languages. When you embed the interface, you&#39;re defining a member variable which can hold data. Any object you assign to that variable will be type-checked to ensure it implements the interface methods. The containing struct doesn&#39;t need to define anything, because the member is required to do that.</p></pre>Ranger_X: <pre><p>I see, that helps make some sense. Thanks!</p></pre>

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

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