You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/blog/15-go-features-you-may-not-already-know.mdx
+31-9Lines changed: 31 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,10 @@
1
1
---
2
-
title: 15 Go Features You May Not Already Know
2
+
title: 15 Go Subtleties You May Not Already Know
3
3
pubDate: 2025-10-09
4
4
description: Some of my favorite tidbits from the past year of working with Go.
5
5
imageDescription: A gopher hiding in the grass.
6
6
heroImage: bded67b5-7fe8-4e2e-b46a-4ba90c1779cd
7
-
slug: 15-go-features-you-may-not-already-know
7
+
slug: 15-go-sublteties-you-may-not-already-know
8
8
tags: ["go"]
9
9
---
10
10
@@ -255,7 +255,9 @@ func main() {
255
255
256
256
## Context-Aware Go Functions
257
257
258
-
In a function that's context aware you should always select on the context for a channel send or receive. This is because you may end up waiting needlessly for that operation to complete despite the context being cancelled.
258
+
In a function that's context aware you should always select on the context, as well as the channel. This is because you may end up waiting needlessly for that operation to complete despite the context being cancelled.
259
+
260
+
For instance, in the below example, the operation runs longer than a timeout:
ctx, cancel:= context.WithTimeout(context.Background(), 1*time.Second)// Only 1 second timeout
287
289
defercancel()
288
290
289
291
contextAwareFunction(ctx)
290
292
}
291
293
```
292
294
293
-
If you don't check the context, your function could block indefinitely on the channel (or any other blocking operation) even though the caller has stopped caring.
295
+
This is a good example of why you need to select on context in channel operations - without it, the function would wait the full 5 seconds, even though the context was cancelled after 1 second.
294
296
295
-
Bonus fact: the Go context is cancelled after an HTTP handler finishes and the response is fully sent, even in success responses. This is why you need to be careful with context propagation. For instance you can introduce race conditions by passing along context to an event publisher from an HTTP request, where the fast HTTP response can cancel the propagated context and prevent the publication of the event. This is an important TIL!
297
+
Bonus fact: the Go context is cancelled after an HTTP handler finishes and the response is fully sent, even in success responses. This is why you need to be careful with context propagation. For instance you can introduce race conditions by passing along context to an event publisher from an HTTP request, where the fast HTTP response can cancel the propagated context and prevent the publication of the event.
296
298
297
299
## Empty Structs
298
300
@@ -346,7 +348,7 @@ In this example, the `Event` struct embeds a `time.Time` field. When marshalling
346
348
347
349
This is true of other methods too, which can lead to weird and hard to track down bugs. Be careful with struct embedding!
348
350
349
-
## The `-` tag for JSON
351
+
## The "`-`" tag for JSON
350
352
351
353
Using the "-" tag when Marshalling JSON will cause the field to be omitted. This is nice if you have private data on a field that's also needed for an API response.
352
354
@@ -387,6 +389,26 @@ This is a contrived example, you obviously wouldn't be this careless with a pass
387
389
388
390
When converting a Time in Go to a String, the stringer automatically appends timezone information, which is why string comparisons won't work. Instead, use the .Equal() method, which compares times: "Equal reports whether t and u represent the same time instant. Two times can be equal even if they are in different locations."
389
391
392
+
```go
393
+
package main
394
+
395
+
import (
396
+
"fmt"
397
+
"time"
398
+
)
399
+
400
+
funcmain() {
401
+
t1:= time.Date(2024, 1, 1, 15, 0, 0, 0, time.UTC)
402
+
t2:= t1.In(time.FixedZone("EST", -5*3600)) // Adds timezone info
403
+
404
+
fmt.Println(t1.String() == t2.String()) // prints false
405
+
fmt.Println(t1.Equal(t2)) // prints true!
406
+
407
+
}
408
+
```
409
+
410
+
This often comes up in testing and continuous integration.
411
+
390
412
## The `wg.Go` Function
391
413
392
414
Go 1.25 introduced a `waitgroup.Go` function that lets you add Go routines to a waitgroup more easily. It takes the place of using the go keyword, it looks like this:
0 commit comments