当前位置:网站首页>An efficient method to get keys and values in context

An efficient method to get keys and values in context

2021-01-23 23:23:07 itread01

We know , stay golang Medium context It's a very important bag , Stores the context of the code activity . We often use WithValue() This method , Go back and forth context in Pass on some key value Information . If we want to get context All of the key and value Or I don't know key I want to get value How to do it ? Here's a comparison hacker For your reference .## Research first , have a look WithValue What happened :```package contextfunc WithValue(parent Context, key, val interface{}) Context { return &valueCtx{parent, key, val}}// A valueCtx carries a key-value pair. It implements Value for that key and// delegates all other calls to the embedded Context.type valueCtx struct { Context key, val interface{}}```WithValue Through the key value Here it is valueCtx Of struct in , Save the data . Through research context We found that , Different Functional context There are different implementations ```package context// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to// implement Done and Err. It implements cancel by stopping its timer then// delegating to cancelCtx.cancel.type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time}// A cancelCtx can be canceled. When canceled, it also cancels any children// that implement canceler.type cancelCtx struct { Context mu sync.Mutex // protects following fields done chan struct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call err error // set to non-nil by the first cancel call}``` But without exception , It's all private struct , At the same time, it links all of the context String together . This situation is not good for what we want to do , We can't put context The interface is converted into an implementation to get the information stored inside key and value, And because there are many implementations , We don't know what's next context Is it right? valueCtx, Do you need to skip .## In that case , We can only use some comparisons hacker The method of the :1. Define your own valueCtx Internal data structure and context It's the same in the package 2. Through unsafe.Pointer() Bypass type detection , Force will context.valueCtx Into our valueCtx3. Get the internal value stored in map in ## Practice starts with customizing our own valueCtx , Copy directly context It's just the realization of :```package maintype valueCtx struct { context.Context key, val interface{}}``` And then force and print :```package mainfunc main() { ctx := context.Background() ctx = context.WithValue(ctx, "key1", "value1") valCtx := (*valueCtx)(unsafe.Pointer(&ctx)) fmt.Printf("key: %v, value: %v", valCtx.key, valCtx.val) // panic: runtime error: invalid memory address or nil pointer dereference}``` It didn't happen as we expected , This is very common in programming , There must be something wrong . At this time, we should go through the materials , To understand what we're still blurring , You'll find out why . But , Since it's an article , I'll just write my conclusion . This starts with the implementation of interface types ,golang The concept and implementation of the interface is more specific and complex , If you want to go further , Please refer to this article Stefno Of [ article ](https://www.cnblogs.com/qcrao-2018/p/10766091.html), It's very thorough and detailed golang Every aspect of the intermediary . Okay , Back to our question , Now for golang The interface of , We just need to know : stay golang Two things are stored in the interface , One is the dynamic type held by the interface , The second is the value of dynamic type . The structure is as follows :```type iface struct { itab, data uintptr}``` That is to say , When we're converting interfaces , In fact, it is to change the above structure into valueCtx , But in fact, we expect the data to be stored in the dynamic type value of the interface , So what we should force is iface.data. To do that , We need to put context First convert to iface, And get some of them data:```package maintype iface struct { itab, data uintptr}type valueCtx struct { context.Context key, val interface{}}func main() { ctx := context.Background() ctx = context.WithValue(ctx, "key1", "value1") ictx := (*iface)(unsafe.Pointer(&ctx)) valCtx := (*valueCtx)(unsafe.Pointer(ictx.data)) fmt.Printf("key: %v, value: %v", valCtx.key, valCtx.val) // output: // key: key1, value: value1}``` This time , We finally got the information .## Next , We need to put context All in key value Get it out of it :```package mainfunc GetKeyValues(ctx context.Context) map[interface{}]interface{} { m := make(map[interface{}]interface{}) getKeyValue(ctx, m) return m}func getKeyValue(ctx context.Context, m map[interface{}]interface{}) { ictx := *(*iface)(unsafe.Pointer(&ctx)) if ictx.data == 0 { return } valCtx := (*valueCtx)(unsafe.Pointer(ictx.data)) if valCtx != nil && valCtx.key != nil && valCtx.val != nil { m[valCtx.key] = valCtx.val } getKeyValue(valCtx.Context, m)}``` By calling back , We can easily traverse all context, At the same time golang Medium nil At the bottom is actually a int Of 0, That's why we put nil be called Zero value . therefore , The exit condition of recursion is also very simple , Is when iface Medium data For 0 When , That means we have found context The last one in . Okay , That's all , If you want to get the specific implementation and demo Example , You can come to my [github](https://github.com/liujh2010/code_demo_of_cnblogs/blob/main/ctx-printer/main.go) Found on the , Thank you for reading .

版权声明
本文为[itread01]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/01/20210123232234158y.html

随机推荐