From 63d437ff65e1be3a19a08ba10208a8b968e1b6ce Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Mon, 25 Nov 2024 15:24:40 -0500 Subject: lume/notes: add go pointer conversion note Signed-off-by: Xe Iaso --- lume/src/notes/2024/go-pointer-constant.mdx | 98 +++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 lume/src/notes/2024/go-pointer-constant.mdx (limited to 'lume/src/notes/2024/go-pointer-constant.mdx') diff --git a/lume/src/notes/2024/go-pointer-constant.mdx b/lume/src/notes/2024/go-pointer-constant.mdx new file mode 100644 index 0000000..8f7855a --- /dev/null +++ b/lume/src/notes/2024/go-pointer-constant.mdx @@ -0,0 +1,98 @@ +--- +title: "Getting a pointer to a constant in Go" +desc: "From least to most hacky" +date: 2024-11-25 +--- + +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: + +```go +_, err = s3c.PutObject(ctx, &s3.PutObjectInput{ + Bucket: "mah-bukkit", + Key: "something", + Body: bytes.NewReader(fileContent), +}) +``` + +Doing this gets you a compile error, because you need a _pointer_ to the string. + +There's several ways to work around this. I'm going to go over them in order from least to most hacky. + +## Make those constants into values + +You can make a pointer to a value, but not a constant. Lift the bucket name and key values into variables: + +```go +bucketName := "mah-bukkit" +key := "something" + +_, err = s3c.PutObject(ctx, &s3.PutObjectInput{ + Bucket: &bucketName, + Key: &key, + Body: bytes.NewReader(fileContent), +}) +``` + +This works in most cases, but you have to declare variables every time. This can look odd. + +## The `aws.String` / `aws.Type` functions: + +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: + +```go +_, err = s3c.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String("mah-bukkit"), + Key: aws.String("something"), + Body: bytes.NewReader(fileContent), +}) +``` + +This works because function arguments are treated as values: + +```go +package aws + +func String(val string) *string { + return &val +} +``` + +## Making your own generic pointer to anything function + +Something else you can do is use Go generics to make a "get me the pointer of this" function: + +```go +func p[T any](val T) (*T) { + return &val +} +``` + +Then you can use it as normal: + +```go +_, err = s3c.PutObject(ctx, &s3.PutObjectInput{ + Bucket: p("mah-bukkit"), + Key: p("something"), + Body: bytes.NewReader(fileContent), +}) +``` + +## The Kubernetes trick + +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: + +```go +raised := &[]string{"foo"}[0] +``` + +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: + +```go +_, err = s3c.PutObject(ctx, &s3.PutObjectInput{ + Bucket: &[]string{"mah-bukkit"}[0], + Key: &[]string{"something"}[0], + Body: bytes.NewReader(fileContent), +}) +``` + +However every step in this is perfectly logical. -- cgit v1.2.3