<pre><code>type Key uint8
var helloKey Key
func InjectHelloToContext(ctx context.Context, hello string) context.Context {
return context.WithValue(ctx, &helloKey, hello)
}
func FetchHelloFromContext(ctx context.Context) string {
return ctx.Value(&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's address, and I think helloKey will have default value 0?</p></pre>bketelsen: <pre><p>you're right it would have a zero value. My bad! But why the address? You'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'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'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't even need a distinguished type, so you only need <code>var key int</code>, so it'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'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'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'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'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'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't, but the common Go idiom is to use a type assertion when you're converting an interface.</p>
<p>Maybe I'm being a little picky, but I've never seen it any other way and I don't see any reason to change context'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'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're really going to debate it, the uint8 or pointer is going to be copied into the interface, so you're still copying 24 or 56 unnecessary bits.</p>
<p>But, like i said, it's just being pedantic and will have no effect on your program.</p>
<p>Point two: is it not? I'll only squash errors and type assertions if I'm the only one editing the code. Otherwise, why not program defensively? I know that even on code bases only I've touched that I'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'm genuinely curious.</p>
<p>At the end of the day this is just quibbling over nothing, really, but I don't see any benefit from breaking from the norm.</p></pre>dsymonds: <pre><p>I've read millions of lines of Go code, and spent a long time helping hundreds of people get up to speed with Go. It's always been fine to have a non-checked type assertion if you are certain it'll be the correct type. If you program overly-defensively it can, in fact, be confusing to readers: they'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's no massive benefit of a pointer over a typed constant here. I'm not claiming that. I'm only disputing <em>your</em> claim that there'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
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传