Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Discourage taking an address of empty structs #141

Open
prashantv opened this issue Feb 25, 2022 · 2 comments
Open

Proposal: Discourage taking an address of empty structs #141

prashantv opened this issue Feb 25, 2022 · 2 comments

Comments

@prashantv
Copy link
Contributor

prashantv commented Feb 25, 2022

2 different objects can have the same address in memory if they are empty structs (0 sized). The Go spec calls this out:

Two distinct zero-size variables may have the same address in memory.

This can still be surprising, and lead to issues when the pointer value is used for comparisons, here's a couple of examples that have some unexpected results.

Add 2 unique elements, but since they have the same address, only a single element is added:

type operation struct{}

var (
  add = &operation{}
  sub = &operation{}
)

m := map[*operation]int{
  add: 1,
  sub: 2,
}
fmt.Println(m)

// Output:
// map[0x553f28:2]

Using context keys using pointers to empty structs which end up being the same,

type contextKey struct{}

var (
  userID    = &contextKey{}
  userEmail = &contextKey{}
)

ctx := context.WithValue(context.Background(), userID, "user-id")
ctx = context.WithValue(ctx, userEmail, "user@example.com")

fmt.Println("userID", ctx.Value(userID))
fmt.Println("userEmail", ctx.Value(userEmail))

// Output:
// userID user@example.com
// userEmail user@example.com

I think taking addresses to empty structs (which I think are the only 0-sized objects) should be discouraged. I'd also like to build a linter for this to flag it automatically.

@mway
Copy link
Member

mway commented Mar 1, 2022

Thanks Prashant!

The point is valid, though I think this is probably a bit niche compared to how (more or less) broadly applicable the other guidance is. Based on how things are structured right now, I'd consider adding two discrete bits:

  1. How to properly define types for context keys to avoid conflation; and
  2. "Memory gotchas" more generally, which would relate more to situations in which types (pointers to zero values, pointers to functions, etc) may have surprising memory addresses or other related behavior

The former could make sense as-is, provided that it's a common enough problem (unclear atm, but very possibly). We've also been talking about adding a section for more advanced and/or "in the weeds" guidance, which the latter would fall nicely into, I think.

@prashantv
Copy link
Contributor Author

Agree that the guidance is very specific, so doesn't make sense at the top-level by itself. I think breaking it apart guidance for context keys, and a separate memory gotchas section makes a lot of sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants