···11+---
22+title: "Getting a pointer to a constant in Go"
33+desc: "From least to most hacky"
44+date: 2024-11-25
55+---
66+77+In Go, sometimes you need to get a pointer to a constant value. This is normally easy, but only if you have a _value_, not a _constant_. Let's say you or a friend are dealing with the AWS S3 API and you need to pass a value to one of the parameters:
88+99+```go
1010+_, err = s3c.PutObject(ctx, &s3.PutObjectInput{
1111+ Bucket: "mah-bukkit",
1212+ Key: "something",
1313+ Body: bytes.NewReader(fileContent),
1414+})
1515+```
1616+1717+Doing this gets you a compile error, because you need a _pointer_ to the string.
1818+1919+There's several ways to work around this. I'm going to go over them in order from least to most hacky.
2020+2121+## Make those constants into values
2222+2323+You can make a pointer to a value, but not a constant. Lift the bucket name and key values into variables:
2424+2525+```go
2626+bucketName := "mah-bukkit"
2727+key := "something"
2828+2929+_, err = s3c.PutObject(ctx, &s3.PutObjectInput{
3030+ Bucket: &bucketName,
3131+ Key: &key,
3232+ Body: bytes.NewReader(fileContent),
3333+})
3434+```
3535+3636+This works in most cases, but you have to declare variables every time. This can look odd.
3737+3838+## The `aws.String` / `aws.Type` functions:
3939+4040+The [`aws` package](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2) exposes some [helper functions](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws#hdr-Value_and_Pointer_Conversion_Utilities) that do this conversion for you. You'll see these in the example code:
4141+4242+```go
4343+_, err = s3c.PutObject(ctx, &s3.PutObjectInput{
4444+ Bucket: aws.String("mah-bukkit"),
4545+ Key: aws.String("something"),
4646+ Body: bytes.NewReader(fileContent),
4747+})
4848+```
4949+5050+This works because function arguments are treated as values:
5151+5252+```go
5353+package aws
5454+5555+func String(val string) *string {
5656+ return &val
5757+}
5858+```
5959+6060+## Making your own generic pointer to anything function
6161+6262+Something else you can do is use Go generics to make a "get me the pointer of this" function:
6363+6464+```go
6565+func p[T any](val T) (*T) {
6666+ return &val
6767+}
6868+```
6969+7070+Then you can use it as normal:
7171+7272+```go
7373+_, err = s3c.PutObject(ctx, &s3.PutObjectInput{
7474+ Bucket: p("mah-bukkit"),
7575+ Key: p("something"),
7676+ Body: bytes.NewReader(fileContent),
7777+})
7878+```
7979+8080+## The Kubernetes trick
8181+8282+Making variables and passing things as arguments to functions aren't the only way to do this, there's also a trick I learned by reading Kubernetes source code. I'll paste an example and then explain how it works:
8383+8484+```go
8585+raised := &[]string{"foo"}[0]
8686+```
8787+8888+This works by creating an anonymous string slice with one member `"foo"`, grabs the first element of that slice, and gets the pointer to it. This makes the code look kinda cursed:
8989+9090+```go
9191+_, err = s3c.PutObject(ctx, &s3.PutObjectInput{
9292+ Bucket: &[]string{"mah-bukkit"}[0],
9393+ Key: &[]string{"something"}[0],
9494+ Body: bytes.NewReader(fileContent),
9595+})
9696+```
9797+9898+However every step in this is perfectly logical.