|
| daniel-thompson wrote:
| The author demonstrates attaching services to a context, which
| afaict goes against the Go authors' usage recommendations in
| https://pkg.go.dev/context:
|
| > Use context Values only for request-scoped data that transits
| processes and APIs, not for passing optional parameters to
| functions.
| [deleted]
| morelisp wrote:
| Don't do it. Don't past non-request-scoped data through contexts,
| and none of these are request-scoped. You can trivially pass such
| values through middlewares using methods or closures.
|
| Also, while I think I know what "unlike Rust, these phantom types
| appear at runtime in Go" is saying, it's saying it in a really
| confusing way. These types do not "appear at runtime"; they are
| exactly like all other types, constructed at compile time and
| _visible_ when interface-boxed at runtime. They 're in no way
| "spectral".
| vore wrote:
| 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.
| cyberax wrote:
| How 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]
| ilyt wrote:
| They 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 wrote:
| 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. Given that, I think you can handwave it a bit and
| regard context variables as goroutine local.
| tommiegannert wrote:
| Not 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/.
| ..
| collinvandyck76 wrote:
| When 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.
| [deleted]
| atombender wrote:
| There's no need to put any dependencies in the context, ever. For
| HTTP servers, the easiest way is just to use a struct to hold the
| dependencies: type Server struct {
| logger *Logger users *UserService // etc.
| }
|
| Assuming we use a router like Chi or similar:
| router.Get("/users", srv.HandleUsers)
|
| ...where HandleUsers is a method on Server.
|
| Some people like to spread handlers in different packages. You
| can of course declare handlers as functions with no receiver
| struct, and pass the data as an argument:
| router.Get("/users", func(w io.ResponseWriter, r *http.Request) {
| userroutes.HandleUsers(srv, w, r) })
|
| For middlewares, e.g. something that parses a session cookie and
| fetches the current user, do exactly the same thing.
| Cyph0n wrote:
| Exactly this. Your dependencies are not request scoped, so
| there is no reason for them to be included in a per-request
| context. Instead, dependencies should be passed in to and
| stored in the struct that uses them.
|
| The data you store in the context should be specific to the
| current request. Think: request ID, user ID, auth info, etc.
| et1337 wrote:
| We use strongly typed contexts for:
|
| - Request-scoped parameters like user ID, request ID, etc.
|
| - Dependency injection (database connection and other IO)
|
| - Cancellation
|
| It works great. The one hairy part is when you need to change the
| context, for example when logging in you take an anonymous
| context and end up with a logged-in context. We use linters to
| ensure that stuff is right, but it still takes some thought in
| the edge cases.
|
| Still, I think those issues are exposing problems in our codebase
| rather than problems with typed contexts.
| candiddevmike wrote:
| Do you use a single context key with a struct or iota keys for
| each value?
| rcme wrote:
| Why do you use it for dependency injection? Why can you not
| pass those parameters directly when instantiating your service?
___________________________________________________________________
(page generated 2023-06-18 23:01 UTC) |