How to convert primitive types to pointers in a common way in Golang?

I know in Golang it uses & to get a pointer of a variable, not for a constant:

// incorrect
a := &"test"

For this suituation, we need to define an extra variable explicity:

// correct
str := "test"
a := &str

I try to define a function to do the converting for every type:

func PStr(s string) *string { return &s }

func PInt(s int) *int { return &s }

a := &PStr("test")

I thought of converting all primitive types to a pointer using generic:

type BasicType interface {
	~string | ~bool | ~int | ~int64
}

func P[T BasicType](t T) *T { return &t }

a := P("test")
b := P(20)

It works, but I wonder if there is an existing function in the standard libraries.

How do you want to use these pointers? You can only get a pointer to a variable, because it allows you to modify the underlying value. A literal is a constant expression, so the compiler doesn’t allow you to get a pointer to it.

Note that in your last example a and b are pointers to copies of "test" and 20. That is because you first pass those literals by value to P. The value is copied to t and then the pointer to t, not the actual function argument, is returned.

1 Like

You can use new() builtin to allocate memory for a variable and get pointer to it in one command.

1 Like

Thanks for your answer. I just want to create a new struct in one line as follows:

type UserQuery struct {
	Age           *int
	MemoNull *bool
}

query := UserQuery{Age: P(30), MemoNull: P(false)}

Anyway, I find my answer here:

it provides a similar way:

func Ptr[T any](v T) *T {
    return &v
}

But I still wonder why the Go team doesn’t provide such a function in the SDK or syntax sugar to get the pointer to a literal.

1 Like

Thanks, Leo, but new is for a type not for a literal in my case.

Because you are not getting a pointer to the literal anyway. Function Ptr first instantiates a (local) variable v from the literal and then returns the pointer to that variable (which probably escapes to the heap because of it, BTW).

There simply is no gain from this operation. Use pointers for their stated purposes in Go: passing by reference when you need to modify something in a function or, in rare cases, to reduce the cost of copying large data structures.

1 Like

Well, first, I just want to get a pointer to a literal string or number in one line instead of

age := 30
memoNull := false
query := UserQuery{Age: &age, MemoNull: &memoNull}

I know the compiler won’t allocate memory for the number 30, so I have to declare the age variable explicitly to get a pointer. Thus instantiating a (local) variable v from the literal is OK for this case.
Second, there is some logic for each field in UserQuery to do only when they are not assigned. So I have to use pointers in the struct to check if the value is nil instead of 0 or empty string.

Isn’t it what was added into database/sql package with go 1.22? Null value, where you can already see if it exists without checking the default.

Thank you Leo for your information, I’m making a framework based on go 1.18 which is the first version to support generic types, and I didn’t know the new feature in 1.22 before.

I checked the new feature, I think they can provide a similar effect but not exactly the same. With

type Null[T any] struct {
	V     T
	Valid bool
}

the UserQuery would be

type UserQuery struct {
	Age      sql.Null[int]
	MemoNull sql.Null[bool]
}

and check if the fields are assigned by checking sql.Null.Valid, right?
This provides the same effect by checking if the pointer is nil in previous code.

But I think this brings some side effects. It takes more memory to create such a struct first, and it’s also a pain for the JSON package to do (de)serialization, as well as reflective creation.

What do you think?

Yes

It’s not true. Your solution with pointers actually requires more memory than this one. Through the discussion some of your answers looked like you treat pointer as a value. But it’s a pointer and your actual value is still somewhere in memory. So, since a struct doesn’t create memory overhead and it’s size is the sum of sizes of the fields. Thus, for example, Null[string] will require 8+1 bytes. At the same time, pointer has a fixed sizes, 4 bytes on 32-bit and 8 bytes on 64-bit. Since x64 is more common, your solution with *string will require 8+8 bytes.

Marshal/Unmarshal are interfaces. You can always implement your own solution for those values, but it’s not a unique thing. There are tons of packages out there, to do it for you. Check this one for example.

Well, at the times I was learning golang, a very smart person told me:

if you are using reflection, you are doing something wrong.

In what world there might be a need to create a struct with pointers with reflect package to use it in sql?! But okay, if it’s actually a need, you can get the value of a string and create Null by yourself. Most part of the time there are no super-duper easy one-line solutions in go. The package, I sent above, already has functions to create its types.

Thanks for the pointer knowledge, Leo, but I think you forgot the memory alignment for the bool in sql.Null. Null[string] will require 8+8 bytes instead of 8+1. And I knew the value assigned to the pointer would require extra bytes.

Here I make a test:

type UserQuery3 struct {
	Name Null[string]
	Age  Null[int]
}
type UserQuery4 struct {
	Name *string
	Age  *int
}

func main() {
	fmt.Println(unsafe.Sizeof("s"))            // 16
	fmt.Println(unsafe.Sizeof(true))           // 1
	fmt.Println(unsafe.Sizeof(5))              // 8
	fmt.Println(unsafe.Sizeof(Null[bool]{}))   // 2
	fmt.Println(unsafe.Sizeof(Null[int]{}))    // 16
	fmt.Println(unsafe.Sizeof(Null[string]{})) // 24

	fmt.Println(unsafe.Sizeof(UserQuery3{}))                           // 40
	fmt.Println(unsafe.Sizeof(UserQuery3{Name: Null[string]{V: "s"}})) // 40

	fmt.Println(unsafe.Sizeof(UserQuery4{}))                                                              // 16
	fmt.Println(unsafe.Sizeof(UserQuery4{Name: P("s")}), unsafe.Sizeof("s"))                              // 16 16
	fmt.Println(unsafe.Sizeof(UserQuery4{Age: P(5), Name: P("s")}), unsafe.Sizeof("s"), unsafe.Sizeof(5)) // 16 16 8
}

In my PC, string requires 16 bytes.
So UserQuery3 will always require 40 bytes, while UserQuery4 requires 16 bytes when all pointers are nil and 40 bytes at most when all fields are assigned.

For most cases, less than half of the struct will be assigned, that’s what I mean by it takes more memory to create UserQuery3.

For JSON data, it will require an extra implementation or package to introduce complexity.

For reflection, what I make is a framework to process the structs defined by users with arbitrary fields, as far as I know, there is no other way to get the field names except reflection.

My bad. Yeah, forgot the alignment.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.