Skip to content

Commit 5a8fbc7

Browse files
Edited Go features/subtlties post
1 parent 0b88e21 commit 5a8fbc7

2 files changed

Lines changed: 32 additions & 10 deletions

File tree

.astro/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ declare module 'astro:content' {
9898
"blog": {
9999
"15-go-features-you-may-not-already-know.mdx": {
100100
id: "15-go-features-you-may-not-already-know.mdx",
101-
slug: "15-go-features-you-may-not-already-know",
101+
slug: "15-go-sublteties-you-may-not-already-know",
102102
body: string,
103103
collection: "blog",
104104
data: InferEntrySchema<"blog">

src/content/blog/15-go-features-you-may-not-already-know.mdx

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
2-
title: 15 Go Features You May Not Already Know
2+
title: 15 Go Subtleties You May Not Already Know
33
pubDate: 2025-10-09
44
description: Some of my favorite tidbits from the past year of working with Go.
55
imageDescription: A gopher hiding in the grass.
66
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
88
tags: ["go"]
99
---
1010

@@ -255,7 +255,9 @@ func main() {
255255

256256
## Context-Aware Go Functions
257257

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:
259261

260262
```go
261263
package main
@@ -270,8 +272,8 @@ func contextAwareFunction(ctx context.Context) {
270272
ch := make(chan string)
271273

272274
go func() {
273-
time.Sleep(2 * time.Second)
274-
ch <- "operation complete"
275+
time.Sleep(5 * time.Second)
276+
ch <- "operation complete" // Takes 5 seconds to send...
275277
}()
276278

277279
select {
@@ -283,16 +285,16 @@ func contextAwareFunction(ctx context.Context) {
283285
}
284286

285287
func main() {
286-
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
288+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) // Only 1 second timeout
287289
defer cancel()
288290

289291
contextAwareFunction(ctx)
290292
}
291293
```
292294

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.
294296

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.
296298

297299
## Empty Structs
298300

@@ -346,7 +348,7 @@ In this example, the `Event` struct embeds a `time.Time` field. When marshalling
346348

347349
This is true of other methods too, which can lead to weird and hard to track down bugs. Be careful with struct embedding!
348350

349-
## The `-` tag for JSON
351+
## The "`-`" tag for JSON
350352

351353
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.
352354

@@ -387,6 +389,26 @@ This is a contrived example, you obviously wouldn't be this careless with a pass
387389

388390
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."
389391

392+
```go
393+
package main
394+
395+
import (
396+
"fmt"
397+
"time"
398+
)
399+
400+
func main() {
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+
390412
## The `wg.Go` Function
391413

392414
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

Comments
 (0)