5 ms·
I'm not a big fan of context variables: they're basically a super clunky kind of goroutine-local global variable, with all the problems you might get with these
by vore 3y ago
I'm not a big fan of context variables: they're basically a super clunky kind of goroutine-local global variable, with all the problems you might get with these kinds of mutable global variables. I would argue that if you're having to pass dependencies down, you should be explicitly dependency injecting them instead rather than implicitly via context.
- ilyt 3y agoThey are not "goroutine local", they are essentially (at least the way they are used) a request-local variable. Which in itself is useful tool. My only real issue with them is lack of type safety so you have to remember which contest var is what. For example I'd like to have User context variable that is set somewhere early if user is authenticated but now at every subsequent call there needs to be a bit of fluff to check whether it is right type, else you risk runtime safety.
- vore 3y agoI think it always a mistake to start a goroutine without passing a context to it, since you are no longer able to cancel the goroutine unless you build your own mechanism to do so. Given that, I think you can handwave it a bit and regard context variables as goroutine local.
- morelisp 3y agoPassing some state into it from outside makes it the opposite of "local".
- ilyt 3y ago>I think it always a mistake to start a goroutine without passing a context to it, since you are no longer able to cancel the goroutine unless you build your own mechanism to do so Not really a problem. If you pass a channel you can close the channel. And cancel isn't magical, still need to handle it from within a goroutine and adding a timeout isn't any more or less complex than handling context cancellation > Given that, I think you can handwave it a bit and regard context variables as goroutine local. That's extremely wrong way to handle it, especially if it is in a bigger app. There is nothing stopping from changing the values in it in other goroutines for example, and one of designed uses of ctx is scatter-gather pattern of spawning multiple parallel tasks that need to be done to respond to request, and other is using ctx to pass data between middlewares (as is often used in web frameworks in Go at least). So you have to at least take care about variable naming across the project and parallel access to them might happen so you need to take care about not running 2 goroutines wanting to write into same variable.
- tommiegannert 3y agoNot sure I understand the typing issue. The standard pattern is to provide an x.Context(ctx, ...) and an x.FromContext(ctx), and those are the only two functions that know about the context key, and they do the type-casting. Since they are the only functions knowing the context key (the key is often a pointer to an unexported variable), you are guaranteed type-safety as long as there is no unsafe code that can pretend-inject the key. https://cs.opensource.google/go/go/+/refs/tags/go1.20.5:src/context/context.go;l=343;drc=61f5a672edd6cce959a47e20dcfb59eaaa5ebd56 https://cs.opensource.google/go/go/+/refs/tags/go1.20.5:src/...
- collinvandyck76 3y agoWhen people mention type safety in this context, they are talking about compiler guarantees. Application code that uses type assertions will never be "type safe" in the way that a compiler can ensure. So while you're correct that if the application code that does the type checking is bug free then the type safety is implied, but is not certain in all cases.
- adamwk 3y agoSure but the interface is typesafe and usually that’s good enough. The two methods of the API (NewContext and FromContext) are typesafe from the perspective of the user.
- ilyt 3y agoWe're talking about context variables which are of type any so you need to check them runtime.
- yencabulator 3y agoThe guarantee you don't get is that foo.FromContext always succeeds. If *Foo was a struct field of your receiver or an argument to the function, the function would be guaranteed to have access to a *Foo.
- cyberax 3y agoHow would you do that with standalone functions? You'll need at least one parameter for cancelation, and one parameter for the logging context, and perhaps one more parameter for tracing. Even passing the single context variable increases clutter a lot.
- deleted 3y ago[deleted]
- deleted 3y ago[deleted]