Can I inject value to context.Context this way?

agolangf · · 598 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<pre><code>type Key uint8 var helloKey Key func InjectHelloToContext(ctx context.Context, hello string) context.Context { return context.WithValue(ctx, &amp;helloKey, hello) } func FetchHelloFromContext(ctx context.Context) string { return ctx.Value(&amp;helloKey).(string) } </code></pre> <hr/>**评论:**<br/><br/>bketelsen: <pre><p>close, but helloKey needs a value</p> <p>change var helloKey Key to var helloKey Key = 1</p></pre>alexanderchenmh: <pre><blockquote> </blockquote> <p>I just need helloKey&#39;s address, and I think helloKey will have default value 0?</p></pre>bketelsen: <pre><p>you&#39;re right it would have a zero value. My bad! But why the address? You&#39;re just using an integer as a key in a map of values.</p></pre>dsymonds: <pre><p>Using a pointer guarantees it is a unique key that another package can&#39;t replicate. If you use a plain int (or a plain <code>Key</code> value) then another package can recreate a key that would compare with <code>helloKey</code>.</p></pre>lansellot: <pre><p>Why use a pointer? Just make <em>key</em> private.</p> <pre><code>type key uint8 var k key ... return context.WithValue(ctx, k, hello) </code></pre> <p>k is private so it can&#39;t be used in other packages. You can use instances of key to get multiple private keys (<em>k2 := key(12)</em> for example).</p></pre>dsymonds: <pre><p>That would work too. If you use the address of a var, you don&#39;t even need a distinguished type, so you only need <code>var key int</code>, so it&#39;s one line shorter. But either work.</p></pre>daveddev: <pre><p>Ah, interesting. I missed that. I expect nil will always be returned unless the same pointer is used in both functions.</p> <p>Edit - Oops, nevermind. It&#39;s a global, so it will work despite being odd.</p></pre>daveddev: <pre><p>Technically, that is possible. However, you are discarding the assertion success/failure bool. Please see this play for clarification in code: <a href="http://play.golang.org/p/RJDvA2Ek4s" rel="nofollow">http://play.golang.org/p/RJDvA2Ek4s</a>, and the documentation here: <a href="https://golang.org/doc/effective_go.html#interface_conversions" rel="nofollow">https://golang.org/doc/effective_go.html#interface_conversions</a>.</p> <p>The net/context documentation gives an example of how to code this (<a href="https://github.com/golang/net/blob/master/context/context.go#L93-L138" rel="nofollow">https://github.com/golang/net/blob/master/context/context.go#L93-L138</a>).</p></pre>alexanderchenmh: <pre><p>I understand. It&#39;s example code. I just want to know whether I can use a global address as a key.</p></pre>dsymonds: <pre><p>It looks fine.</p></pre>barsonme: <pre><p>Except he&#39;s using a pointer to a variable for the key as well as possibly allowing a panic in his second function...</p> <p>Should be a constant, not a pointer, and he should have a type check.</p></pre>dsymonds: <pre><p>A pointer to a variable is fine for the key. What do you think will go wrong?</p> <p>A panic in the second function isn&#39;t possible unless some code in this same function misbehaves or they export the key.</p></pre>barsonme: <pre><p>Nothing other than it causes you to pause for a second. Also, if you&#39;ll allow me to be pedantic, a pointer is larger than a uint8.</p> <p>Also, it allows the value of the key to be changed by misbehaving code.</p> <p>A panic <em>is</em> possible if other code in the same directory misbehaves. There might be a 99% chance it won&#39;t, but the common Go idiom is to use a type assertion when you&#39;re converting an interface.</p> <p>Maybe I&#39;m being a little picky, but I&#39;ve never seen it any other way and I don&#39;t see any reason to change context&#39;s conventions.</p></pre>dsymonds: <pre><p>A pointer is larger than a uint8, but the key gets stored in an interface{}, and so the size difference won&#39;t actually matter.</p> <p>It is not common Go style to be overly protective against misbehaving code, and even less so within a given package.</p> <p>I have certainly seen it use a pointer to a variable plenty of times, and done it myself.</p></pre>barsonme: <pre><p>Regarding the first point, I was just being pedantic. If we&#39;re really going to debate it, the uint8 or pointer is going to be copied into the interface, so you&#39;re still copying 24 or 56 unnecessary bits.</p> <p>But, like i said, it&#39;s just being pedantic and will have no effect on your program.</p> <p>Point two: is it not? I&#39;ll only squash errors and type assertions if I&#39;m the only one editing the code. Otherwise, why not program defensively? I know that even on code bases only I&#39;ve touched that I&#39;ve accidentally asserted incorrect types or stuffed a string into an interface that was blindly asserting []byte. It happens.</p> <p>What benefit does a pointer to a variable add over a constant? I&#39;m genuinely curious.</p> <p>At the end of the day this is just quibbling over nothing, really, but I don&#39;t see any benefit from breaking from the norm.</p></pre>dsymonds: <pre><p>I&#39;ve read millions of lines of Go code, and spent a long time helping hundreds of people get up to speed with Go. It&#39;s always been fine to have a non-checked type assertion if you are certain it&#39;ll be the correct type. If you program overly-defensively it can, in fact, be confusing to readers: they&#39;ll pause to wonder why you are checking when it appears like the operation can never fail.</p> <p>If you want an example from the standard library, read src/fmt/print.go and see how *pp is handled when retrieved from the sync.Pool.</p> <p>There&#39;s no massive benefit of a pointer over a typed constant here. I&#39;m not claiming that. I&#39;m only disputing <em>your</em> claim that there&#39;s something wrong with using a pointer.</p></pre>schumacherfm: <pre><p>I can recommend to implement an empty struct if you provide just one context helper set in a package and const() with iota for multiple context values within the same package [edit: ur use multiple types instead of consts if you like it ;-) ]:</p> <pre><code>type ctxServiceKey struct{} func FromContext(ctx context.Context) (s *Store, ok bool) { s, ok = ctx.Value(ctxServiceKey{}).(*Store) return } func NewContext(ctx, s *Store) context.Context { return context.WithValue(ctx, ctxServiceKey{}, s) } </code></pre></pre>

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

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