Storing arbitrary types

blov · · 527 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hello all,</p> <p>Python dev picking up golang again.</p> <p>I&#39;m toying with writing an Arguments module, that uses the flag pkg.</p> <p>I want to define a struct as such:</p> <pre><code>type argument struct { name string value ??? argType reflect.Kind } </code></pre> <p>I want the value to have type of argType, and I want to be able to be able to call .Value() on my argument to retrieve the appropriately typed value, without having to do any extra type assertion.</p> <p>Is there an idiomatic way to do this? Coming from a dynamically typed language, I&#39;ve forgotten all my statically typed prowess.</p> <p>Should I avoid using reflection? The only way I see of doing this is with reflection, setting value as type interface{}, and using type assertion to get appropriate values type back, but that&#39;s messy imo. I don&#39;t want the user who calls .Value() to have to do some further type assertion.</p> <p>Any help would be greatly appreciated!</p> <hr/>**评论:**<br/><br/>skidooer: <pre><p>I would advocate that it is best to explain what you want to do, rather than how you are doing it. It is possible that a completely different approach would better solve for what you are trying to accomplish.</p> <p>With that said, on the limited information here, why not provide methods for the various types that may be returned? .Int() returns an int, .String() returns a string, etc. That way the caller doesn&#39;t have to worry about asserting the type. You can do it for them.</p></pre>ChristophBerger: <pre><p>I second this. Be aware of <a href="http://xyproblem.info/">the XY Problem</a>.</p></pre>trchttrhydrn: <pre><p>I just did this to myself. </p> <p>I started with a situation where I needed to have a cheap way of mutual exclusion on numerous entities, so I used a uint32 with a simple atomic compare-and-swap spinlock.</p> <p>Then I realized I needed to support RLock() and Lock(), and more than this, I needed a queue&#39;d sort of fairness, to ensure that numerous RLock()&#39;s couldn&#39;t starve a Lock().</p> <p>So fine, now I had an array-based queueing lock, allowing each locker to wait on their own core-local variable at a given index in the array. After each check, they would sleep for a bit before trying again to see if they had reached the queue head. Each Unlock sets the locker&#39;s array variable to 0 and sets the next one to 1 (the next locker will now wake up and see it&#39;s at the queue head).</p> <p>I realized I had in the end just reimplemented the functionality (if not the implementation) of RWMutex, and poorly, since it caused 100 times more voluntary context-switches than RWMutex when I benchmarked it. LOL. My simple lightweight uint32 had become a poor replacement for what I should have used all along.</p> <p>Well I guess it&#39;s not <em>strictly</em> the same problem. More like a problem of not reevaluating the possible solutions as my requirements expanded, and continuing to modify my solution which was reached when requirements were different.</p></pre>iamdevquestionmark: <pre><p>Yeah, this seems to fit my issue to a T lol. Thanks :) I&#39;ll reply to poster above</p></pre>iamdevquestionmark: <pre><p>Fair enough, here&#39;s a more indepth description of what I want to do: </p> <p>I have a binary which can be called with certain options/arguments. I wanted to make argument definition as easy as possible, and have all these arguments checked (for correctness/required/etc) in one place by looping over all defined arguments. </p> <p>Furthermore, I wanted to use these argument values without having to do any type assertion or reflection (for which - my initial assumptions are correct - this is impossible). </p> <p>Here&#39;s an SSCCE, on playground: <a href="https://play.golang.org/p/a7Pi_XQgcZj" rel="nofollow">https://play.golang.org/p/a7Pi_XQgcZj</a> </p> <p>I&#39;d heartily appreciate any suggestions. I know it&#39;s messy, i wrote it last night to try out whether or not i could try arbitrary values.</p></pre>beknowly: <pre><p>Code generation is your friend.</p> <p>Check out how urfave/cli does it: <a href="https://github.com/urfave/cli/blob/master/flag_generated.go#L60" rel="nofollow">https://github.com/urfave/cli/blob/master/flag_generated.go#L60</a></p></pre>danredux: <pre><p>This is impossible. The compiler needs to know the function signature. Even using generics would require passing in the type. Use the empty interface and type assert it.</p> <p>Better yet, if you&#39;re wanting to learn Go, I&#39;d suggest you stop trying to force it to be like Python. While right now you&#39;re unhappy you can&#39;t just return anything and have the runtime handle this &#34;types&#34; stuff, later you&#39;ll love knowing that your variables actually hold what you think they hold. You&#39;ll hate Go if you try to write it like Python.</p></pre>iamdevquestionmark: <pre><p>Great points. These are pitfalls I&#39;m trying to avoid, hence the question about how to go about it idiomatically. </p> <p>Thanks :)</p></pre>earthboundkid: <pre><blockquote> <p>Should I avoid using reflection?</p> </blockquote> <p>Yes.</p></pre>spacemit: <pre><p>Isn&#39;t reflection in Go really fast? (Basically just dereferencing a pointer)</p></pre>earthboundkid: <pre><p>That’s not why it sucks. It sucks because you’re trying to do an end run around the type system and in almost all cases (everything except serialization/deserialization) that’s a mistake and you’ll have a better system by going with the type system. </p> <p>The same applies in Python, by the way. For Python, you should almost never use metaclasses or look at types at all. </p></pre>ihsw: <pre><blockquote> <p>without having to do any extra type assertion.</p> </blockquote> <p>This is not really possible, your return type has to be <em>something</em>. As you said you can perform type-switches with <code>interface{}</code> being the type of <code>value</code> but there&#39;s not much else you can do here. Welcome to Go, going against the grain is the most effective way to frustrate yourself.</p></pre>iamdevquestionmark: <pre><p>Yeah, I feared as much. It&#39;ll just be easier to store all parsed args as strings, and convert before using them as necessary. </p> <p>Thanks for the quick reply! :)</p></pre>ihsw: <pre><blockquote> <p>strings</p> </blockquote> <p>Why not just byte arrays? If you&#39;re going to go the marshaling/unmarshaling route, you can make it more interesting and store json blobs or protobuf blobs.</p></pre>iamdevquestionmark: <pre><p>Yeah I&#39;ve had a couple of suggestions for using a byte array. </p></pre>mixiegen: <pre><pre><code>type argument struct { name string value interface{} } </code></pre> <p>Go doesn&#39;t support custom generic, so type assertion is required. </p></pre>weberc2: <pre><p>This is a runtime type anyway, so generics wouldn&#39;t help.</p></pre>beknowly: <pre><p>?</p> <pre><code>data Argument a = Argument { name :: String , value :: a } readRandomAssArg :: (Read a) =&gt; (String, String) -&gt; Argument a readRandomAssArg (name, raw) = Argument { name = name , value = read raw } helloInt :: Argument Int helloInt = readRandomAssArg (&#34;hello&#34;, &#34;1234&#34;) helloBool :: Argument Bool helloBool = readRandomAssArg (&#34;hello&#34;, &#34;True&#34;) </code></pre></pre>weberc2: <pre><p>Those aren’t runtime types; they’re still static.</p></pre>beknowly: <pre><p>Yeah that&#39;s the idea with generics... Things that would otherwise be runtime types can be encoded in the type system to make them compile time.</p></pre>weberc2: <pre><blockquote> <p>I want to be able to be able to call .Value() on my argument to retrieve the appropriately typed value</p> </blockquote> <p>I think you&#39;re confusing static and runtime types. Because <code>value</code> can be anything at runtime, it should have the type <code>interface{}</code> (the static type for a value that can have any time at runtime). Any time you want to assert that the runtime type in an <code>interface{}</code> is a particular static type, you need to use a type assertion. And to be clear, this isn&#39;t a limitation in Go, it&#39;s fundamental to types in general (as a mathematical concept) so it applies to every programming language (including languages like Python and JavaScript, where everything is implicitly an <code>interface{}</code>).</p></pre>iamdevquestionmark: <pre><p>You are correct. I did indeed muddle up static and runtime types. Thanks for the reply!</p></pre>

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

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