Refactoring help requested - unset pointer registers as nil in current function, but not in called functions

agolangf · · 651 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Any ideas on what I&#39;m doing wrong here?</p> <p><a href="https://play.golang.org/p/8_bLwky0Rs">Playground Example</a></p> <p>I was attempting to consolidate some testing code into a single method which would trigger a GET or POST based on whether a map of parameters was passed in. The problem is that it appears that the &#34;zero&#34; value for the pointer to a strings.Reader object (the request body) is not actually being interpreted as a nil value when it is passed as a method parameter to the http.NewRequest function (even though it tests as being nil in the current function). Explicitly setting the variable to nil also results in a panic.</p> <p>On the other hand, if I call <code>http.NewRequest(method, route, nil)</code>, there is no panic.</p> <p>There is, of course, the workaround of branching on the passed map and having two calls to <code>http.NewRequest</code>, but that seems a bit redundant.</p> <hr/>**评论:**<br/><br/>pappogeomys: <pre><p>This is in the FAQ, but usually arrises from error values: <a href="http://golang.org/doc/faq#nil_error">http://golang.org/doc/faq#nil_error</a></p> <p>The zero value for <code>*strings.Reader</code> is being wrapped in an <code>io.Reader</code> when you pass it into the NewRequest function. This means although the <code>io.Reader</code> internal &#34;value&#34; is <code>nil</code>, the &#34;type&#34; is set to <code>*strings.Reader</code>, making the interface as a whole not equal to <code>nil</code>. </p> <p>You need to explicitly pass in <code>nil</code> to be safe. <a href="https://play.golang.org/p/u5b58KZsOE">https://play.golang.org/p/u5b58KZsOE</a></p></pre>dominic_failure: <pre><p>Wow. Talk about an unforeseeable side effect of reflection. One would expect that a nil value, when <a href="https://golang.org/ref/spec#Calls" rel="nofollow">passed by value</a> into a function would be equivalent to passing the nil literal.</p> <p>I&#39;ve been operating under the assumption that interfaces were there purely for the compiler and type system, guess this is a wakeup call to dig even deeper into reflection, even if I rarely use it.</p> <p>In any case, thanks for pointing me in the right direction!</p></pre>pappogeomys: <pre><p>You don&#39;t need to go into reflection to understand this (though the same principals apply, you need to understand Go interfaces.). Read that faq entry carefully, as it&#39;s explained in full.</p> <p>And of course I forgot another option which <a href="/u/anossov" rel="nofollow">/u/anossov</a> mentions, declare your <code>requestReader</code> as the interface type itself -- <code>io.Reader</code>.</p></pre>dominic_failure: <pre><p>Sure, but the underlying issue is caused by a decision about how to make interfaces support reflection by implementing them as a box for the underlying value, not the interface specification itself (which is remarkably <a href="https://golang.org/ref/spec#Interface_types" rel="nofollow">simple</a>, in spite of the examples).</p> <p>This indicates that there may be other implementation niggles caused by supporting reflection; being cognizant of all the possible actions through reflection will let me infer more about the underlying implementation.</p></pre>pappogeomys: <pre><p>Well, a language with reflection needs to support reflection, so that&#39;s always a given.</p> <p>But here, interfaces themselves don&#39;t contain anything extra just for reflection. You may be thinking of <a href="http://golang.org/ref/spec#Type_assertions" rel="nofollow">Type Assertions</a>, which are a core part of the language, and don&#39;t rely on reflection. Without the original type (and <a href="http://golang.org/ref/spec#Method_sets" rel="nofollow">method set</a>) from the interface, you couldn&#39;t statically assert an interface to a type or another interface, hence removing a lot of their constructive value (the standard library alone probably contains thousands of type assertions).</p></pre>rebel581: <pre><p>Interesting to know, but is there a reason why Go was designed this way? It seems like a huge gotcha and I don&#39;t understand why all nil comparisons aren&#39;t considered the same. I can&#39;t think of a reason where having an interface with a type but a nil value is helpful other than creating potential places to panic. In this case, the calling function performs a check for nil and then panics because a nil value was passed in.</p></pre>pappogeomys: <pre><p>First, a typed nil is still a valid value, and you can call methods on a nil value that don&#39;t dereference a pointer receiver. If a nil value was wrapped in an interface, it can be still useful to determine what that value was.</p> <p>An interface is also a value, and an interface value that contains <code>{nil, Type}</code> is not the same as an interface that contains <code>{nil, nil}</code>. Having them both evaluate equal to <code>nil</code> would be more confusing. Even more confusing would be if <code>{nil, TypeA} == nil</code> and <code>{nil, TypeB} == nil</code>, but <code>{nil, TypeA} != {nil, TypeB}</code>, breaking transitive equality. </p> <p>It&#39;s a gotcha that Go programmers all see at some point, but it&#39;s in the FAQ, and once you understand interfaces it becomes very clear as to why it behaves the way it does.</p></pre>anossov: <pre><p>Instead of passing an expicit <code>nil</code> you can also declare <code>var requestReader *strings.Reader</code> as <code>var requestReader io.Reader</code>.</p></pre>dominic_failure: <pre><p>Elegant. Thank you!</p></pre>klauspost: <pre><p>Have a look at <a href="https://golang.org/doc/faq#nil_error" rel="nofollow">Why is my nil error value not equal to nil?</a>. </p> <p>Once you send a nil &#34;<em>strings.Reader&#34; it is transformed to an interface with type &#34;</em>strings.Reader&#34; and value &#34;nil&#34;, and as the FAQ states: &#34;Such an interface value will therefore be non-nil even when the pointer inside is nil.&#34;</p> <p>I fixed it up. Also, no need to send pointers to maps: <a href="https://play.golang.org/p/4ZaPD4leNr" rel="nofollow">https://play.golang.org/p/4ZaPD4leNr</a></p></pre>dominic_failure: <pre><p>Thanks for the reminder on the map pointer!</p></pre>

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

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