Could anyone help me with some reflection voodoo please?

agolangf · · 389 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I have a function that receives <code>v interface{}</code> as argument which can be <code>*T</code> or <code>[]*T</code>. I&#39;d like that function to modify <code>v</code> for both cases. The <code>*T</code> case is working but I&#39;d like some help for the <code>[]*T</code> case.</p> <p>Here&#39;s some code: <a href="https://play.golang.org/p/tb6Yed5TCd">https://play.golang.org/p/tb6Yed5TCd</a></p> <p>(Reflection is never clear I know but in this case I have no choice.)</p> <hr/>**评论:**<br/><br/>ston1th: <pre><p>I have a solution: <a href="https://play.golang.org/p/MfGJT1oOhN" rel="nofollow">https://play.golang.org/p/MfGJT1oOhN</a></p> <p>But please keep in mind that this is not recommended.</p> <p>Edit: simpler solution</p></pre>nesigma: <pre><p>Amazing! You are a true reflection-dark-arts-voodoo master! :D Thanks a lot!</p></pre>mordfustang21: <pre><p>Couldn&#39;t you use a type switch? <a href="https://play.golang.org/p/iOryUx4ozN" rel="nofollow">https://play.golang.org/p/iOryUx4ozN</a></p></pre>nesigma: <pre><p>I can&#39;t use a type switch because T is not known by foo and the helper returns <code>[]interface{}</code>. Besides the problem is not how to distinguish between slice and and struct. That is taken care off. The problem is how to fill <code>v</code> with data in case <code>v</code> is a slice. In other words I am trying to figure out how to do the <code>v = s</code> part of the code.</p></pre>: <pre><p>[deleted]</p></pre>nesigma: <pre><p>The goal is to fill <code>v</code> with data when it is passed as an argument in foo. Returning aka <code>v := foo()</code> or <code>v := helper()</code> is not allowed.</p></pre>: <pre><p>[deleted]</p></pre>nesigma: <pre><p>Well then I should have also added that the signature of <code>foo(v interface{})</code> should also be unmodified and that helper has to be used inside of foo.</p> <p>The reason is that I am trying to create a function that fills <code>v</code> with data for both cases <code>*T</code> and <code>[]*T</code> in the same exact way. In practice that means:</p> <p>Case <code>*T</code></p> <pre><code>t := new(T) foo(t) // now t is filled with data </code></pre> <p>Case <code>[]*T</code></p> <pre><code>var t []*T foo(t) // now t is filled with slice data </code></pre> <p>The <code>helper</code> func is the provider of data for the second case.</p> <p>The first case is solved and now I am trying to figure out how to do the second case.</p></pre>: <pre><p>[deleted]</p></pre>nesigma: <pre><p>I do not know the type of <code>t</code>, that&#39;s why I named the type <code>T</code>. Sorry for the confusion.</p> <blockquote> <p>What is the purpose of foo()?</p> </blockquote> <p>The purpose of foo is to fill <code>v</code> with data in the same exact way for both cases. Imagine there are two data providers which I cannot change.</p> <p><code>helperOne(v interface{})</code></p> <p><code>helperSlice() []interface{}</code></p> <p>So the purpose of foo is to allow to use those two functions inside it seamlessly as I described above.</p></pre>: <pre><p>[deleted]</p></pre>tv64738: <pre><p>Seems like you are missing the fundamentals of slice behavior, and what append actually does, and reflection is preventing you from realizing that.</p> <p>Mutating a local copy of a slice isn&#39;t very useful.</p> <p><a href="https://blog.golang.org/slices" rel="nofollow">https://blog.golang.org/slices</a></p></pre>nesigma: <pre><blockquote> <p>Mutating a local copy of a slice isn&#39;t very useful.</p> </blockquote> <p>What do you mean? Is it impossible for <code>v</code> to be filled with data in case of a slice?</p></pre>tv64738: <pre><p>Appending to a copy of a slice doesn&#39;t change the original slice.</p></pre>nesigma: <pre><blockquote> <p>Appending to a copy of a slice doesn&#39;t change the original slice.</p> </blockquote> <p>Ok and how can I modify the original <code>v</code> slice then?</p></pre>tv64738: <pre><p>You don&#39;t. Read the blog post I linked to earlier.</p></pre>nesigma: <pre><blockquote> <p>You don&#39;t. Read the blog post I linked to earlier.</p> </blockquote> <p>I did but I still don&#39;t get it.</p> <p>What if I pass a pointer to a slice as an argument?</p></pre>cathalgarvey: <pre><p>A slice is a pointer to an underlying array. You can append to the slice and it will append to the array if there is room, or reallocate if there is not.</p> <p>If there is room, great: the slice is equivalent to the array. If not, you can&#39;t consider them equivalent. You can use <code>len</code> and <code>cap</code> to get the length and total capacity of the underlying array (currently) under a slice. Note slices dynamically reallocate using the <code>append</code> function if needed.</p> <p>Back to your core Q: it sounds like you want generics, but Go hates Generics. So your options are: use code-generators to mimic Generics (several options exist out there), find another way, or learn Rust instead. :P</p></pre>nesigma: <pre><blockquote> <p>A slice is a pointer to an underlying array. You can append to the slice and it will append to the array if there is room, or reallocate if there is not.</p> </blockquote> <p>That is not correct. According to <a href="https://blog.golang.org/slices" rel="nofollow">https://blog.golang.org/slices</a> a slice is a struct value holding a pointer and a length. </p> <p>This is most likely the reason why passing <code>[]*T</code> as an argument to the function in my code (through <code>v interface{}</code>) makes it impossible to change the slice header. The reason is because the slice struct gets copied or something like that. I do not understand it 100% myself.</p> <blockquote> <p>or learn Rust instead</p> </blockquote> <p>Rust is way too complex for me. I mean I already have trouble understanding some Go stuff.</p></pre>cathalgarvey: <pre><p>Sorry, you are correct: I was misusing the word &#34;Pointer&#34;. Well, AFAIK a slice <em>is</em> a literal pointer, because it&#39;s nullable, but you&#39;re correct that the referred-to data is a struct.</p></pre>tv64738: <pre><p>That would work, but would read a differently from typical Go (it&#39;s not <code>append(&amp;s, x)</code>).</p> <p>Better advice comes after you talk about actual use cases.</p></pre>nesigma: <pre><p>I am trying to write a function that wraps up the functionality of two data providers in the exact same way.</p> <p>One provider accepts *T as an argument and fills it with data.</p> <p><code>helperOne(v interface{})</code></p> <p>The other provider returns []interface{} and it meant to fill []*T with data.</p> <p><code>helperSlice() []interface{}</code></p> <p>So I am trying to create a function <code>foo(v interface{})</code> that no matter what the user throws in the argument (<code>*T</code> or <code>[]*T</code>) it uses the correct helper inside and fills in the data in both cases in the exact same way, seamlessly with no returning.</p> <p>In any case, <a href="https://www.reddit.com/r/golang/comments/66qwet/could_anyone_help_me_with_some_reflection_voodoo/dgm9t4g/" rel="nofollow">this</a> is the solution I was looking for!</p></pre>tv64738: <pre><p>That&#39;s not a use case..</p></pre>victrolla: <pre><p>Sorry I&#39;m on mobile so I may be missing something obvious. </p> <p>I&#39;m unclear about why reflection is needed here. It seems to me that your function signature could change to accept variadic arguments. </p> <p>&#39;func foo(args interface{}...) { // operate on args as a slice of interface types }&#39;</p> <p>Am I missing something obvious here?</p></pre>nesigma: <pre><p>This is a very cool idea but unfortunately it doesn&#39;t work.</p> <p>New Signature: </p> <pre><code>func foo(v ...interface{}) {} </code></pre> <p>New Call: </p> <pre><code>var t []*T foo(t...) </code></pre> <p>Gives:</p> <pre><code>cannot use t (type []*T) as type []interface {} in argument to foo </code></pre></pre>

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

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