Integer context keys

June 4, 2025

We’re looking at different types for context keys.

So far, we’ve looked at empty structs (struct{}), and found it to be less than ideal.

Today, let’s consider integer context keys.

This seems handy, right?


type contextKey int

const (
  KeyUserID int = iota
  KeyTransactionID
  KeyMessageID
  .
  .
  .
  KeyFoo
)

Let’s see how it stacks up to our 5 criteria:

  1. Must be comparable.

    ✅ No problem!

  2. Use minimal membory.

    int doesn’t have as small a memory footprint as struct{}’s zero bytes, but it’s still pretty small. One could potentially use int8 if this is really important, though it’s likely not worth it.

  3. Should be readable.

    Here’s where numeric keys can suffer. While this convention is fairly readable in code, it can make for a pain when debugging.

    fmt.Printf("failed to read context key %s", KeyMessageID)
    // prints:  failed to read context key 3
    

    You could somewhat mitigate this problem by adding a String() method on the custom type:

    func (k contextKey) String() string {
    	switch k {
    	case KeyUserID:
    		return "KeyUserID"
    		/* ... */
    	}
    }
    

    Meh. That improves the situation, but at what cost? And it only works for places where a String() method is even considered. Not perfect, by any means.

  4. Easy to declare multiple keys per package.

    ✅ Super easy!

  5. Nice if it works with const

    ✅ Definitely!

So in conclusion… using an int (or other numeric type) for your context keys can work, functionally, but it suffers for debugability. Not the biggest drawback in the world, but if we have another alternative that checks all the boxes, that’s probably better… so let’s keep looking.


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Context key type: Final recommendation

First, a correction! An astute reader pointed out that I made a small mistake in my post on June, 10, with regard to string context keys. My code example showed: type contextKey string const ( KeyUserID = "user_id" KeyTransactionID = "transaction_id" KeyMessageID = "message_id" ) But it should have been: type contextKey string const ( KeyUserID contextKey = "user_id" KeyTransactionID contextKey = "transaction_id" KeyMessageID contextKey = "message_id" ) This is a nasty kind of bug, because the code will continue to work as expected—just without any protection from key collisions!


Struct context keys

First off, an apology for being rather unreliable with my “daily” emails. I’m in the middle of some traveling (was in the UK and Netherlands last week, and will be on a US road trip starting tomorrow), so I’m writing when I have a spare moment, which isn’t that often. I’ve been talking about finding the ideal type for a context key. So far I’ve looked at empty structs (e.g. struct{}), integers, and strings.


String context keys

By now we’ve looked at empty structs (struct{}), and integers as possible context key types. Today, let’s consider string context keys. type contextKey string const ( KeyUserID contextKey = "user_id" KeyTransactionID contextKey = "transaction_id" KeyMessageID contextKey = "message_id" ) How does this stack up against our 5 criteria? Must be comparable. ✅ Check. Use minimal memory. ✅ Using a string will typically use a bit more memory than an integer (typically 32 bytes vs 16), but still quite minimal.

Get daily content like this in your inbox!

Subscribe