This notion was addressed in #9097, which was shut down rather summarily. Rather than reopen it, let me take another approach.

When &S{} was added to the language as a way to construct a pointer to a composite literal, it didn't quite feel right to me. The allocation was semi-hidden, magical. But I have gotten used to it, and of course now use it often.

But it still bothers me some, because it is a special case. Why is it only valid for composite literals? There are reasons for this, which we'll get back to, but it still feels wrong that it's easier to create a pointer to a struct:

p := &S{a:3}

than to create a pointer to a simple type:

a := 3
p := &a

I would like to propose two different solutions to this inconsistency. Now it has been repeatedly suggested that we allow pointers to constants, as in

p := &3

but that has the nasty problem that 3 does not have a type, so that just won't work.

There are two ways forward that could work, though.

Option 1: new

We can add an optional argument to new. If you think about it,

p := &S{a:3}

can be considered to be shorthand for

p := new(S)
*p = S{a:3}

or

var _v = S{a:3}
p := &_v

That's two steps either way. If we focus first on the new version, we could reduce it to one line by allowing a second, optional argument to the builtin:

p := new(S, S{a:3})

That of course doesn't add much, and the stuttering is annoying, but it enables this form, making a number of previously clumsy pointer builds easy:

p1 := new(int, 3)
p2 := new(rune, 10)
p3 := new(Weekday, Tuesday)
p4 := new(Name, "unspecified")
... and so on

Seen in this light, this construct redresses the fact that it's harder to build a pointer to a simple type than to a compound one.

This construct creates an addressible form from a non-addressible one by explicitly allocating the storage for the expression. It could be applied to lots of places, including function returns:

p := new(T, f())

Moreover, although we could leave out this step (but see Option 2) we could now redefine the & operator applied to a non-addressible typed expression to be,

new(typeOfExpression, Expression)

That is,

p := &expression 

where expr is not an existing memory location is now just defined to be shorthand for

p := new(typeOfExpression, expression)

Option 2

I am more of a fan of the new builtin than most. It's regular and easy to use, just a little verbose. But a lot of people don't like it, for some reason. So here's an approach that doesn't change new. Instead, we define that conversions (and perhaps type assertions, but let's not worry about them here) are addressible. This gives us another mechanism to define the type of that constant 3:

p := &int(3)

This works because a conversion must always create new storage. By definition, a conversion changes the type of the result, so it must create a location of that type to hold the value. We cannot say &3 because there is no type there, but by making the operation apply to a conversion, there is always a defined type.

Here are the examples above, rewritten in this form:

p1 := &int(3)
p2 := &rune(10)
p3 := &Weekday(Tuesday)
p4 := &Name("unspecified")

Discussion

Personally, I find both of these mechanisms attractive, although either one would scratch the itch. I propose therefore that we do both, but of course the discussion may end up selecting only one.

Template

Would you consider yourself a novice, intermediate, or experienced Go programmer?

I have some experience.

What other languages do you have experience with?

Fortran, C, Forth, Basic, C, C++, Java, Python, and probably more. Just not JavaScript

Would this change make Go easier or harder to learn, and why?

Perhaps a little easier, but it's a niche problem.

Has this idea, or one like it, been proposed before?

Yes, in issue #9097 and probably elsewhere.

If so, how does this proposal differ?

A different justification and a new approach, with an extension of new.

Who does this proposal help, and why?

People annoyed by the difficulty of allocating pointers to simple values.

What is the proposed change?

See above.

Please describe as precisely as possible the change to the language.

See above.

What would change in the language spec?

The new operator would get an optional second argument, and/or conversions would become addressible.

Please also describe the change informally, as in a class teaching Go.

See above.

Is this change backward compatible? Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit.

Yes. Don't worry.

Show example code before and after the change.

See above.

What is the cost of this proposal? (Every language change has a cost).

Fairly small compiler update compared to some others underway. Will need to touch documentation, spec, perhaps some examples.

How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?

Perhaps none? Not sure.

What is the compile time cost?

Nothing measurable.

What is the run time cost?

Nothing measurable.

Can you describe a possible implementation?

Yes.

Do you have a prototype? (This is not required.)

No.

How would the language spec change?

Answered above. Why is this question here twice?

Orthogonality: how does this change interact or overlap with existing features?

It is orthogonal.

Is the goal of this change a performance improvement?

No.

If so, what quantifiable improvement should we expect?

More regularity for this case, removing a restriction and making some (not terribly common, but irritating) constructs shorter.

How would we measure it?

Eyeballing.

Does this affect error handling?

No.

If so, how does this differ from previous error handling proposals?

N/A

Is this about generics?

No.

If so, how does this differ from the the current design draft and the previous generics proposals?

N/A

Comment From: seebs

How much would it break things to let new() take either a type or an expression which has an unambiguous type? Thus, new(int) or new(fnReturningInt()), or possibly even new(int(3)), but not new(3) because that hasn't got an unambiguous type? This would address the stuttering, I guess?

I think I dislike the implicit allocation on taking the address of non-addressible things, because I think basically all this means is that we will finally be able to replace the "loop variable shadowing and goroutines" thing with "i took the address of a thing in a map but writes to it aren't changing it" as the most frequently asked question about Go. If it only happens with &conversion(), though, that seems significantly more clear; conversion is clearly logically creating a new object, even if you convert a thing to exactly the type it already is.

So far as I can tell, object names and type names are the same namespace, it's not like C's struct-tag madness, but at any given time a given identifier refers only to one or the other.

Comment From: clausecker

Alternatively, what's the problem with adding composite literals of simple types, like int{3}?

Comment From: JAicewizard

If we have this new new(typeOfExpression, Expression), would it be possible to do new(int32, int64(5))?? Not necessarily this specific case, but for any expression that does not match the specified type will there be an implicit conversion?

I think I like the new idea better, it is more explicit about what happens when you are taking an address of an unaddressable value.

Adding int{3} feels more of a workaround to me, than a solution. Adding a new way to do the same thing, just to solve a problem.

Comment From: faiface

What about generalizing the second approach and simply allow taking the address of a function result?

p := &f(...) // for any f

Conversions are just special functions, so this would cover them.

Comment From: eaglebush

I like the new proposal option to handle just the simple types and initialize to a value. I have created functions just for this. With the proposal approved, some of the constructs will be soon like this:

i := new(int, 42)

...much shorter than package prepended codes like this:

i := stdutil.NewInt(42)

Consequently...

i := new(int, func() int {
   r := rand.New(rand.NewSource(99))
   return r.Int()
}())

Comment From: peterbourgon

I am more of a fan of the new builtin than most. It's regular and easy to use, just a little verbose. But a lot of people don't like it, for some reason.

I prefer &T{...} to new whenever possible, because it permits both construction and initialization in a single expression, which I think is important. The only circumstance where it didn't work is addressed by this proposal's second option. Nice! +1 from me.

As far as I can tell, this would make it possible to express all valid Go programs without using the new builtin. Bonus challenge: do the same for make :) I think it would boil down to extending the struct literal initialization syntax in some way that could cover just these 4 things:

make(chan T, n)
make(map[T]U, n)
make([]T, n)
make([]T, n, m)

Comment From: benhoyt

@peterbourgon Probably we shouldn't derail this to try to get rid of make. :-) My preference is also the &int(3) type conversion syntax, as I too almost never use new -- not because I dislike new, just because it's not usually necessary. I also want to link to other previous discussions for reference (aside from #9097):

  • I opened a similar issue a few years ago (#22647), which I think is useful because it has a brief "experience report" in the description -- for example, the AWS SDK has functions like aws.Int and aws.String to work around the lack of this feature.
  • There's also a 2014 proposal to allow new(value) in a Google doc here. I don't think that's as good or orthogonal as new(T, value), but wanted to link it for the history.

I used to be in favor of &expression (it that Rob's option "1a"?), but now I think there are too many concerns with it. For example, it means & would mean something different for an expression vs a variable: &expression would always give a new address, but &variable would always given the same address -- that seems non-intuitive. Related to this is the point @seebs made that you could then write &m[k], which would make it look like map entries are addressable, but they're not. For these reasons, I think plain &expression is a bad idea, despite it being nice and terse.

Comment From: faiface

@benhoyt If you only restrict & to variables and function calls, not arbitrary expressions, it's quite consistent because a result of a function call will naturally have a fresh address.

Comment From: benhoyt

@faiface Yeah, I think that would be fine -- it doesn't have the problems with &arbitraryExpression that I noted. My issue #22647 actually grew out of trying to type &time.Now() when I was fairly new to Go.

Comment From: clausecker

When supporting taking the address of return values, the question on whether returning makes a copy of the return value obtains. For example, consider code like this:

func addressTaker(x int, z **int) (y int) {
    y = x
    *z = &y
}

func example() {
    var ptr *int
    x := &addressTaker(42, &ptr)

    // at this point, does x == ptr hold?
}

@benhoy Not really in favour of the new(value) proposal as it opens the can of worms that is having to distinguish between types and expressions in the parser (at least it seems so).

Comment From: clausecker

I also kinda wonder why the obvious &int{3} idea is not mentioned. Though yes, the type conversion comes with the obvious advantage (or possibly disadvantage?) of being more flexible with the type of its argument. Supporting both uses might even be sensible (one for when you want a type conversion to happen, possible with a go vet if there is none) and one for when you do not want a type conversion.

Comment From: mcandre

Rob, don't tell me about such a gap. I was implementing Bliss interface for so long.

Comment From: robpike

@clausecker Because why add a new construct (&int{3}) when you can use an existing one?

Comment From: clausecker

@robpike Compound literals too are an existing construct and taking the address of them is already legal. So it's as much “adding a new construct” as the &int(3) idea is; in both cases the rules need to be made more lenient to support a case that was previously not allowed with no syntactical changes; in case of &int(3) taking the address must be made legal, in case of &int{3} using a composite literal for a scalar.

Comment From: ninedraft

The new(T, value) variant has an unpleasant feature: for boolean and string values, it adds excessive visual noise. For example: new(bool, true), new(string, "bottle of ram").

As far as I understand, only numeric literals have a problem with unambiguous type inference.

With the above in mind, & + typecast seems like a more viable approach for me, if it will allow us to omit type in string and boolean cases.

Examples:

_ = &int(42)
_ = &true
_ = &"brains"

type Name string
_ = &Name("what's my name?")

type Count int64
_ =&Count(100500)

Comment From: thejerf

The Go 2 playground permits the function:

func PointerOf[T any](t T) *T {
    return &t
}

If I break this issue up into cases, I end up with either "I need this zero times in a module" (by far the dominant case), "I need this once or twice" in which case I would just take the couple of extra lines, and "I need this all over the place" in which case, either define that function or pull it in from somewhere once the generics are out. If one is using this a lot one may prefer a shorter name than PointerOf, I was just going for maximum clarity over length.

I'd suggest just waiting for generics to drop and writing/providing that function.

Comment From: zkosanovic

@ninedraft

With the above in mind, & + typecast seems like a more viable approach for me, if it will allow us to omit type in string and boolean cases.

But you can't omit the type. The description clearly says that type conversion will be addressable, not the values themselves.

It would have to be:

_ = &bool(true)
_ = &string("brains")

And TBH I'm fine with that. Having something like &"foobar" feels a bit... odd.

But either way, having the Option 2 would be very cool IMO.

Comment From: smasher164

I'd suggest just waiting for generics to drop and writing/providing that function.

While it's true that generics would allow you to write the PointerOf function, I think this (second) proposal would make it much easier to learn the language. Having to write/use a function for something that has first-class syntax with composite literals is counterintuitive.

Comment From: sanggonlee

If I can add voice here, I would much prefer option 2 than 1. The fact that simple type literals had deeper underlying types was hidden away from convenience syntax anyway (for example, 3 having int type as default while it could also have been int32). Syntax in option 1 seems a bit awkward passing two args separately, one for type and one for expression even though the two are inherently bound with each other. Technically the same goes for option 2, but in this case at least it gives a stronger visual cue that the 3 belongs to the int32 type in &int32(3), which seems more consistent with type conversion form used widely.

Comment From: sethvargo

Do we have any data on new() vs &{} usage in the wild? Anecdotally (and supported by others on the threads), I feel like &{} is far more common than new, but it would be excellent if we had some data to back that up.

I'm definitely preferential to option 2 (&int64(11)).

Comment From: rh-kpatel4

Why not &(int64(11))? This is proper scoping to take the output of () and return pointer to it &()?

Comment From: FiloSottile

What about generalizing the second approach and simply allow taking the address of a function result?

go p := &f(...) // for any f

Indeed, I understand the difference between conversion and function calls, but I feel like people learning Go will be confused by &int(3) working while &add(1, 2) doesn't.

Function calls have defined types, so I can't think of any issue with taking their pointer, and I definitely had to be reminded by the compiler that it wasn't allowed a few times.

I never use new() simply because I don't want to choose between two ways of doing the same thing, so I am partial to doing just Option 2, but the last part of Option 1 feels like a better landing place for & completeness.

Comment From: earthboundkid

I like that Jerf's PointerOf function adds nothing to the language itself. It could be added to the builtins as newof or newval or something. With the addressTaker example above, it allocates a new pointer for x, which is unambiguous.

Comment From: bcmills

All of the proposed options seem better than the status quo, but still have the downside of requiring types to be written out explicitly even when they are obvious from the value. Compare:

    d := time.Millisecond
    p1 := &d  // No noise from types!

vs.

    p1 := new(time.Duration, time.Millisecond)
    p2 := &time.Duration(time.Millisecond)

In contrast, the generic approach (https://github.com/golang/go/issues/45624#issuecomment-822487722) does not stutter on types, but requires the introduction of a new name for the generic function.

So I wonder if it would be preferable to add a generic builtin instead:

    d := ptrTo[time.Duration](time.Millisecond)

or

    d := ptrTo(time.Millisecond)

I don't feel strongly about the specific name, but I think the ergonomics of a generic function are much nicer than the proposed ergonomics of new.

Comment From: fkarakas

When initially @chai2010 made the first proposal, it was considered as "adding a third syntax seems not a good plan" now that rob pike propose it, it is wonderful !!! so go maintainers you can do whatever you like....

Comment From: clausecker

One thing to keep in mind about the &foo(x) syntax is that by design, it cannot catch type mismatches. For example, if you accidentally use a constant belonging to the wrong enumeration, there's no way for the compiler to catch that as you have an explicit type cast there. If it was &foo{x} (possibly supported as an additional option), the compiler could reject such code as being wrongly typed. Do we really want to introduce mandatory quasi implicit casting for this feature?

Comment From: rsc

The overloading of & for "take address of existing value" and "allocate copy of composite literal" has always been unfortunate. An alternative to expanding the overloading of & would be to overload new instead, so that it is the generic ptrTo function as well as the original new(T), as in new(1). Then &T{...} can be explained retroactively as mere syntactic sugar for new(T{...}).

Comment From: rsc

@fkarakas:

When initially @chai2010 made the first proposal, it was considered as "adding a third syntax seems not a good plan" now that rob pike propose it, it is wonderful !!! so go maintainers you can do whatever you like....

For what it's worth, Rob clearly credits the proposal you mentioned and says that he thinks it was closed too quickly. Restarting a discussion is clearly better than never changing our minds as new evidence arrives and never admitting when we may have made a mistake. I talked at length about context and how added context or new experience can lead to different outcomes in my talk at https://blog.golang.org/toward-go2. None of us are perfect, and whether an idea is adopted inevitably depends as much on whether the time is ripe for that idea as on the details of the idea itself. Cheers.

Comment From: randall77

The problem that I see with &int{1} is that it begs the question: what does just int{1} mean? Is it the same as int(1)? Why are there two ways to say the same thing?

Comment From: rsc

Another problem with &foo{x} is what it means when foo and x are both type []interface{}. Then there is only one way to say two different things.

Comment From: Laremere

Does this proposal limit this behavior to literals? The forms used are not obviously limited. If they're not: Comparing and contrasting multiple examples with this proposal:

Go today:

a := 1
b := &a
*b = 2
fmt.Print(a,*b) // 2 2

Using cast to pointer:

a := 1
b := &int(a)
*b = 2
fmt.Print(a, *b) // 1 2

Using extended new:

a := 1
b := new(int, a)
*b = 2
fmt.Print(a, *b) // 1 2

ptrTo with generics:

a := 1
b := ptrTo(b)
*b = 2
fmt.Println(a, *b) // 1 2

All three of these examples show that the behavior is subtly different than taking a pointer to a local variable. It would be reasonable to use this new construct in a more complicated context (ie, more lines between the different statements in the examples). For a novice or someone otherwise not familiar with the syntax, it's important it's obvious the code is doing something different, and what that difference is.

I feel that casting performs worst in this test. A cast may look like a function call, but it doesn't feel like one. So this proposal breaks some new ground on the specifics of their behavior. I think extended new performs better on this test, and ptrTo performs best. They simply follow the convention of a value type passed to a function call.


Alternatively,

Now it has been repeatedly suggested that we allow pointers to constants, as in p := &3 but that has the nasty problem that 3 does not have a type, so that just won't work.

It's not obvious to me why this wouldn't work.

If I never specify a type in

v := 3
p := &v

Then why does p := &3 need a type? What's wrong with using the same rules as declaring a variable with no type specified?

Comment From: rsc

p := &3 doesn't work because it must be limited to some narrow set of forms. Otherwise the meaning of &f().x is different for f() returning pointer-to-struct and f() returning struct. Similarly &m["x"] is a compile error today but would silently make a copy tomorrow rather than produce a pointer to the value in a map. All of that would be incredibly confusing and the source of many subtle bugs.

Comment From: seebs

Gosh this whole thing turns out to be ridiculous, we already have a completely transparent and easy to type way to do what people mean when they try to write x := &int{3}:

x := &((&[1]int{3})[0])

I think I'd like to put in a vote for "allow {} initializers for non-compound types by treating them as sort of an implicit [1] of their type". (Oh, but I do see the difficulty with cases like []interface...)

But that leads to the question: should you be allowed to specify the key?

x := &int{0: 3}

Comment From: xaionaro

Personally I use:

p := &[]int{v}[0]

May be it just makes sense to allow shorthanding of []int{v}[0] to {v}:

p := &{v}

Thus: * p := &{v} points to the copy of v. Here, v might be anything, for example myFunc(): p := &{myFunc()}; or 3.1416: &{3.1416} (will be float64*, since f := 3.1416 is float64). * p := &v points to the v itself.

Though I'm not sure if Go is about syntax sugar.

Comment From: seebs

So, in times of longago, the C standard just sort of handwaved a ton of stuff by saying "well, obviously, any object of a type is also an array of one of that type". So you're allowed to bracket initializers all over the place:

int i = (int){8};
int *ip = &(int){8};
int j = {8};

Of course, they don't have interface{} to deal with.

Right now, the reason that &literal{...} works is sort of a subtle side-effect of garbage collection and escape analysis; you're allowed to declare objects, and if they escape, they can get allocated, so if the pointer escapes, it causes an allocation, and otherwise it's not a "real" allocation any more than any other variable declaration is, and we're just using a stack address. Whereas new() sort of carries the implication that it's going to "be allocated" even if in fact the pointer doesn't escape and doesn't need heap allocation.

I think that letting new(expr) work like p := new(T); *p = expr is probably reasonable and harmless, and given that, I might well use new(expr) more and &literal{} less, because it would be clearer what it was doing and why. The reason I mostly don't use new is that it's stuttery and requires me to distinguish between allocating the zeroed object and populating it.

Comment From: Laremere

@rsc Thanks, I see the issue in that. That's not quite what I was asking, but working through asking the question more precisely, I now see the issue. Adding here for the benefit of the proposal or others who don't see the problem:

Today the go spec states:

Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal's value.

It seems reasonable that "composite" could be removed from that sentence. That is, any Literal (as defined here) value can be initialized as a pointer to the literal's value.

The only literal values beyond composite literals are numbers, runes, strings (all are BasicLit), and functions (FunctionLit).

Runes, strings, and functions all have well defined types and would all work fine. Custom types would be a bit awkward, but otherwise still work:

p4 := (*Name)(&"unspecified")

That leaves numbers, which already have well defined rules from determining their type when none is specified. eg, &3 would be *int, and &1.2 would be *float64. However how would you get a pointer to a byte? Typically a cast is used to coerce the number constant to resolve into the desired type. However &byte(3) is not getting the address of a Literal, it's getting the address of a result from a cast.

Without the issue of numbers, I think it would be a reasonable extension of the current behavior, making composite literals less special. It would still be the case that & has two meanings, just one of them would be slightly more powerful.

I suppose you could allow for (*byte)(&3), where &3 is a "pointer number literal" which would be resolved to a pointer to a specific number type using similar rules to how plain numbers are resolved. That certainly adds complexity equal to or greater than the main proposal, though it would be limited to just number literals. I'm not sure if I like it or not.

Comment From: nemith

As a point of why this would be nice, thrift uses pointers for optional fields in the generated code with nil representing a missing field (zero value is not an option). So thrift library contains a bunch of functions to pointerize literals.

https://github.com/apache/thrift/blob/master/lib/go/thrift/pointerize.go

Moving forward with generics perhaps this could be dealt with a optional wrapper type, or a generic pointerize function.

Comment From: DeedleFake

I've wanted this on more than one occasion, but most of the time that I've wanted it I wanted it to give a value to something optional, such as when initializing struct fields:

type Config struct {
  Address *string
}

// ...

c, err := CreateClient(Config{
  Address: &string("localhost:12345"), // Doesn't work, obviously.
})

I have to wonder if this issue will disappear automatically over time once generics are in as optionality is technically only a side effect of pointers, which is why a lot of things also return a boolean to signal validity of their primary return instead of just returning a pointer. Generics, though, can create a more properly signaled optionality:

type Optional[T any] struct {
  v T
  ok bool
}

func Some[T any](v T) Optional[T] {
  return Optional[T]{v: v, ok: true}
}

func None[T any]() Optional[T] {
  return Optional[T]{ok: false}
}

type Config struct {
  Address Optional[string]
}

// ...

c, err := CreateConfig(Config{
  Address: Some("localhost:12345"),
})

And then, after finishing writing this, I took a look at the new comment that loaded in right above... You beat me to it, @nemith.

Comment From: slrz

The new extension looks very nice and clean. Probably worth it even without introducing the &expression shorthand for new(typeOfExpression, expression).

Comment From: travisjeffery

I prefer adding the new parameter---e.g. new(int, 3). Looks a lot cleaner and simpler from a language standpoint by not overloading &.

Comment From: mdempsky

I like either option and would support adding both. Within option 1 though, I favor simply new(3) rather than new(int, 3), as several have suggested above already.

How much would it break things to let new() take either a type or an expression which has an unambiguous type?

It would not break anything. The parser and type checker already need to be able to distinguish whether e1(e2) is a conversion or function call depending on whether e1 is a type expression or value expression.

Thus, new(int) or new(fnReturningInt()), or possibly even new(int(3)), but not new(3) because that hasn't got an unambiguous type?

3 has an unambiguous type: the default type int. Only the value nil has no default type.

Comment From: HALtheWise

I would be in support of Option 2 or the extension to all function calls mentioned several times in this thread because it most obviously feels like simply removing an existing restriction, rather than adding any new behavior that needs explaining. Go doesn't generally encourage or make use of variadic functions with different behavior depending on their argument count, and when I hear of a two-argument form of "new" I intuitively expect it to behave like the multiple argument form of make(), which is the only other weird built-in like that today. Option 1 doesn't really do that, and as a result adds some extra mental overhead to remember a rule I almost never use, or for new users to look up what it means when they come across it.

I know that @rsc wishes that everyone had standardized on the new() form rather that &t{}, but my sense is the latter is more common today, and we should not try to fight that too hard.

Comment From: icholy

Allowing the following 2 forms would address the majority of use-cases without any of the footguns:

&AnyLiteral
&Type(AnyLiteral)

As @Laremere pointed out, it's also a minimal change to the spec.

Comment From: smasher164

uses pointers for optional fields in the generated code with nil representing a missing field (zero value is not an option).

This is the same approach taken with many of the GraphQL and Avro libraries in Go. I would venture to say any serialization or RPC framework encounters this issue. Codebases end up either pulling in or redefining functions like PtrTo[Int|Float64|...].

Comment From: ysmood

How about:

type Cube struct {
    Size int
}

a := new(Cube{Size: 10})

b := new(10) // by type infer it's int

var c int64 = new(10) // tell compile what type we want

d := new("string")

e := new(1.0) // float64

Comment From: rogpeppe

To reiterate this comment a bit, Limbo had an builtin-function named ref which did exactly that. In Go-with-generics, it could be defined as:

// ref returns a pointer to the value of t.
func ref[T any](t T) *T {
    return &t
}

Given this possibility, I don't see that there's any need to change new or the language syntax itself to accommodate this functionality.

We could define a built-in function with exactly this signature and behaviour (ptr ?) which would fulfil the requirements of this proposal in a more ergonomic and flexible way than proposed (no need to mention the type name; can operate fine on any expression including function calls).

We could even do that before generics land in the language without problem.

So all these would work:

ref(123)   // *int
ref(make([]string, 0, 3))   // *[]string
ref("hello")  // *string
ref(ref(string))  // **string
ref(os.Stdin.Name())

For what it's worth, I still like the name ref even though Go, unlike Limbo, doesn't call pointers "ref". Here's the above in the playground: https://go2goplay.golang.org/p/UV0z1TxjxRh

Comment From: rogpeppe

FWIW I suggested the above solution back in 2016.

Comment From: ziutek

I think @rsc and @ysmood proposal about overloading new built-in function fits best into the language.

If the x := new(int) can be treated as the short form of

var a int
x := &a

then x := new(10) can be the short form of

a := 10
x := &a

@ysmood, in my opinion the

var c int64 = new(10)

should return "type mismatch" error.

I don't know what to do with

var c *int64 = new(10)

It probaly require to introduce something like untyped int pointer to the language. Without such addition we need to write it like this

var c *int64 = new(int64(10))

Comment From: davecheney

I’d like to throw my hat into the ring for option 2, i := &int(3)

Despite the pain that proto has inflicted on Go users, I don’t think the use case is so general that it requires extending new.

Comment From: ziutek

The problem with overloading new I see is:

A := 56
x := new(A)

If the x := new(A) is far away from A := 56 it's not obvious does A is a type or constat/variable.

Comment From: carleeto

I prefer the two parameter version of new.

Yes, it is a little more typing, but it requires you to be explicit about the type and therefore, your intent:

x := new(int,3) x := new(int8,3) x := new(uint16,3)

That said, if all you wanted was an int, I don't see the harm in supporting a single parameter version too:

x := new(3)

Or, for that matter float64:

x := new(3.4)

Comment From: rogpeppe

I'm not so keen on overloading the first parameter of new, as there are no other cases in the language (AFAIK) where a built-in function takes either a type or an expression as an argument.

Neither do I like the idea of adding an optional extra parameter to new when most use cases can avoid mentioning the type name.

Given that we can implement this functionality with nothing beyond what's already proposed for the language, I vote strongly for the "less magic" approach of defining a generic built-in function which will become still less magic when generics actually arrive.

The choice of name is tricky though: I'm not keen on ptrTo because no other language built-in uses camel case; ref doesn't fit with any other existing naming: and ptr uses a contracted form which isn't seen elsewhere (except for the rarely used println, I guess).

Comment From: ruyi789

int x=1,y=2,z=3 or var (x=1,y=2,z=3} int

if()//support it

Comment From: earthboundkid

The choice of name is tricky though: I'm not keen on ptrTo because no other language built-in uses camel case; ref doesn't fit with any other existing naming: and ptr uses a contracted form which isn't seen elsewhere (except for the rarely used println, I guess).

At the risk of devolving into 🚲-ing, I like newof or newval because the returned value is not a pointer or reference to any existing value. It's a pointer to the copy made by the function call. Having new in the name makes it clear that e.g. ptr := newof(mymap[key]) is not equivalent to (the illegal) ptr := &mymap[key] because it's a new pointer.

Comment From: urbanishimwe

I would prefer &int(3). I think we would need to allow this syntax: &T. like p := &int and value at p address will be the zero value of int type. this is because we already have new(T) which does pretty much the same thing.

Comment From: hardboiled

My team has a lot of configuration data from our users that is similar to but does not exactly mirror internal API endpoints that use this configuration data.

This results in a lot of situations where structs composed of pointer value members need to have sane assignments for defaults, especially in tests. Because go doesn't currently support inlining non-struct pointer assignments, we have situations like this:

// example init statement from a test
multiAz := true
storageEncrypted := true
dbConfig := &DatabaseConfig{
    Identifier:       "id",
    InstanceClass:    "db.t3.small",
    InstanceType:     "mysql",
    EngineVersion:    "1.1.1",
    MultiAz:          &multiAz,
    StorageEncrypted: &storageEncrypted,
}

It would be nice to update this test to use the following format in the future.

dbConfig := &DatabaseConfig{
    Identifier:       "id",
    InstanceClass:    "db.t3.small",
    InstanceType:     "mysql",
    EngineVersion:    "1.1.1",
    MultiAz:          &bool(true),
    StorageEncrypted: &bool(true),
}

It definitely would improve readability and code quality in our code IMO. Hope this example was useful to others here to explain why this could be helpful, and thanks for debating the merits here!

Comment From: dolmen

Edit: this comment is redundant with https://github.com/golang/go/issues/45624#issuecomment-822594892

Original content Note that **pointer expressions** to any value **already exist**. They are just ugly. Example (see [on the Go Playground](https://play.golang.org/p/78bRuT-u5TZ)):
// Pointer to an int64 value (3)
int64Ptr := &(&(struct{ int64 }{3})).int64

fmt.Printf("%T %v\n", int64Ptr, *int64Ptr)

myfunc := func() string { return "foo" }

// Pointer to a value returned by a func
stringPtr := &(&(struct{ string }{myfunc()})).string

fmt.Printf("%T %v\n", stringPtr, *stringPtr)
Edit: shorter expression, thanks to @mdempsky:
int64Ptr := &[]int64{3}[0]

Comment From: dolmen

A common workaround is to use a package named ptr which exposes conversion functions for each type.

boolPtr := ptr.Bool(true)

I don't think that new(bool, true) would be better than what has become an idiom.

Examples: * go.uber.org/thriftrw/ptr * github.com/aws/smithy-go/ptr * github.com/AlekSi/pointer * others...

Comment From: xaionaro

A common workaround is to use a package named ptr which exposes conversion functions for each type.

It is impossible. If I will create a custom type then it will be unknown to any of those libraries.

Comment From: dolmen

It is impossible. If I will create a custom type then it will be unknown to any of those libraries.

If you create a custom type T, you can as well create its pointer constructor: func ptrT(t T) *T { return &t }. And it doesn't have to be in the same package. A little copying is better than a little dependency.

Comment From: mdempsky

@dolmen

A little copying is better than a little dependency.

That proverb is about making sure decisions about code structure weigh the cost of adding dependencies. It's not meant to suggest that copying code is inherently good or desirable, as you seem to apply it here. As evidence of that, notice that this proposal was submitted by the same person who coined that phrase (or at least popularized it).

Comment From: deanveloper

I’d like to throw my hat into the ring for option 2, i := &int(3)

Despite the pain that proto has inflicted on Go users, I don’t think the use case is so general that it requires extending new.

I agree with this, many builtin function signatures are already hard to read, especially when there are optional parameters. It would be unfortunate to dirty up new’s signature into something like make’s. I would much prefer to adapt & to work like it does for structs than change new’s signature to have optional arguments.

I honestly would like to get rid of new altogether, it’s a builtin function which I am personally not a big fan of. I feel that builtin functions are best for defining operations on builtin types, and for functions impossible to define because of a lack of generics. new serves no purpose other than saving a temporary variable, and discourages from people from using new as a variable name (which is a very nice variable name to be able to use)

Comment From: davecheney

If you create a custom type T, you can as well create its pointer constructor

If you declare a new type T, then you can use &T{} or new(T) expressions to gain a pointer to a T.

ISTM that the various ptr packages can solve this problem for all the 20 something universe types without extending the lanaguge itself.

Comment From: dolmen

@mdempsky My reference to the proverb was on point. Packages such as go.uber.org/thriftrw/ptr are just collections of func ptrT(v T) *T { return &v } definitions. Those functions can be aggegated in a package for convenience, or just defined locally in your code when you need them without adding a dependency.

Edit: Additional comment redundant with a previous comment about a generic `ptrTo` (but with code examples) Also, with generics, we will be able to define a `ptr` function once for all.
func ptr[T any](v T) *T {
    return &v
}

intPtr := ptr(3)
fmt.Printf("%T %v\n", intPtr, *intPtr)

floatPtr := ptr(3.14)
fmt.Printf("%T %v\n", floatPtr, *floatPtr)

xPtr := ptr(map[string][1]struct{bool; string; *int}{"foo": {{true, "bar", nil}}})
fmt.Printf("%T %v\n", xPtr, *xPtr)
[See on the go2go Playground](https://go2goplay.golang.org/p/cqsC8Mf8U2M)

Comment From: ayang64

i really like that option 2 makes the syntax for obtaining the address of literals (composite or otherwise) consistent. i think extending new or adding a function or keyword leaves that inconsistently dangling so hopefully at least option 2 goes forward.

i don't think we need to extend new() if we can get the address of any literal and i definitely don't think we need a new function or keyword.

Comment From: ayang64

I think we would need to allow this syntax: &T. like p := &int and value at p address will be the zero value of int type. this is because we already have new(T) which does pretty much the same thing.

i think what we're talking about it getting the address of a value not a type. i don't see how getting the address of a type is meaningful -- that is, what do you call the & operator when applied to a type? also, we already have foo := (*string)(nil).

Comment From: davecheney

@dolmen what's the use case for a pointer to a map?

xPtr := ptr(map[string][1]struct{bool; string; *int}{"foo": {{true, "bar", nil}}})

Comment From: kylelemons

Option 1a, new(typeOfExpression, Expression)

This is a bit repetitive, so while I think it does prevent needing helper functions or libraries, I don't think it's worth the addition of the second parameter.


Option 1b, p := &expression

This is convenient but I am concerned about the implications for addressbility and orthogonality. Consider:

a := &m[k]
a := &m.(T)
a := &f()

Today, none of these are addressable.

When I teach Interfaces in Go classes, I spend a lot of time talking about addressability, because a common issue I see is when new Gophers try to pass values when a pointer is necessary to satisfy an interface, and I think the concept is important to understand.

Briefly, I usually have a portion that goes something like this:

when you write
  t.M()

the compiler can help you:
   | (*T).M   |  (T).M
---+----------+----------
 T | (*t).M() | t.M()
*T | t.M()    | (&t).M()

However, when you store a value in an interface:

  var i interface{M()}
  i = t // only legal sometimes depending on Method Sets
  i.M()

The compiler can (conceptually) help you only sometimes:
   | (*T).M   |  (T).M
---+----------+----------
 T | (*i).M() | i.M()
*T | i.M()    | ????

The compiler can't (correctly) write
  (&i).M()

But, with this proposal, it would sort-of be legal to "take the address of the value in an interface", and I feel like it starts to get a bit blurry and non-orthogonal, which might make it even harder for new folks to really grok method sets and addressability.


Option 2: &type(C)

If we think that we need a solution here, this would be my choice. It's a bit repetitive, but I think it maintains the orthogonality of the syntax, and it makes it more clear (to my reading) that the code is asking for a pointer to a newly allocated memory location, rather than how &f() might imply that it is evaluating to the address of something that already exists in the heap.

I would personally advocate for restricting the value to being a compile-time constant, at least to start with.


Since they're not officially part of the proposal, I won't talk too much about the other suggestions, but briefly:

  • new(v) (i.e. changing the first parameter of new) seems strictly worse than adding a new builtin to me.
  • Adding a new builtin ala ptr(v) to subsume the packages would be interesting, but that name is already often used.
  • Letting folks wait until Generics and make their own generic PtrTo seems fine, and those can live along with the other helpers that already exist, especially in the domains (like protobuf) that often need them.

Comment From: dolmen

@urbanishimwe wrote:

I think we would need to allow this syntax: &T. like p := &int and value at p address will be the zero value of int type. this is because we already have new(T) which does pretty much the same thing.

This could change the meaning of existing code (or at least add confusion in reader's mind) because this statement is already valid if a variable with the same name as the type exists in the scope. So I don't think this is a good idea.

// This code works now
var int int
p := &int

See on the Go playground

While this example is contrived, it is much more common to have variables named like a named type.

Example:

// In package scope:
type date string

// Deep in a function
var date time.Time
p := &date

Comment From: dolmen

@davecheney wrote:

what's the use case for a pointer to a map?

As a general question:

var m map[string]bool
p := &m
json.Unmarshal([]byte(`null`), p)
json.Unmarshal([]byte(`{"foo": true}`), p)

Go Playground

In the context of this proposal (pointer to a non zero value expression), I don't see any use case, as well as for pointers to func/interface/channel/pointer. I could not even find a niche use case.

Comment From: deanveloper

@urbanishimwe wrote:

I think we would need to allow this syntax: &T. like p := &int and value at p address will be the zero value of int type. this is because we already have new(T) which does pretty much the same thing.

This could change the meaning of existing code (or at least add confusion in reader's mind) because this statement is already valid if a variable with the same name as the type exists in the scope. So I don't think this is a good idea.

// This code works now var int int p := &int

See on the Go playground

While this example is contrived, it is much more common to have variables named like a named type.

Example:

``` // In package scope: type date string

// Deep in a function var date time.Time p := &date ```

wouldn’t &int and &date still point to the variables rather than the types still? nothing changed.

Comment From: rogpeppe

@davecheney

what's the use case for a pointer to a map?

There might not be a use case for a pointer to a map, but there's definitely a use case for a pointer to a slice, which has similar issues: https://play.golang.org/p/fXlCMX6EYUr

type S struct {
    // Slice gets omitted even when the slice itself is non-nil.
    Slice []int  `json:",omitempty"`
    // PtrSlice is omitted exactly when it's non-nil, even
    // if the slice is empty.
    PtrSlice    *[]int `json:",omitempty"`
}

@kylelemons

Adding a new builtin ala ptr(v) to subsume the packages would be interesting, but that name is already often used.

I'm not sure it's used as much as you suspect. I looked in my $GOPATH (approx 25 million lines of Go code) and found only 1994 uses of ptr as an identifier in 235 packages. Do you think it would be a problem in practice?

For comparison, I did the same search for ref and found 7059 uses across 1897 packages, so it seems like ptr wins by that metric.

I also looked for ptrTo and found no uses at all.

Comment From: clausecker

We could also name the function dup and have it work as a generic shallow-copy function. This would satisfy the requirements of this issue and be obvious in function to the reader (as it makes a copy of the argument). Issues with untyped operands remain though.

Comment From: jalavosus

@rogpeppe I'm slightly confused as to why you'd need a pointer to a slice, since slices are already nil-able. I did up a quick playground example for this (and please excuse the variable names, I haven't slept in a day or so): https://play.golang.org/p/9XO6Q0ZI1mI, where a freshly unmarshaled struct with an omitempty-d slice is nil.

Comment From: thejerf

The use case for "a pointer to anything that you wouldn't normally take a pointer to" is as a parameter for atomic.CompareAndSwapPointer. Niche, absolutely, and swapping a channel pointer still sounds like bad design, but the rest can happen.

Comment From: quenbyako

This is a wonderful change. I would also want to remind you about #12854, since these are very related things (i.e., it may not be necessary to explicitly indicate the type of the value to which the pointer is needed.

Guys how thumbs down be like:  

Comment From: rogpeppe

@rogpeppe I'm slightly confused as to why you'd need a pointer to a slice, since slices are already nil-able. I did up a quick playground example for this (and please excuse the variable names, I haven't slept in a day or so): https://play.golang.org/p/9XO6Q0ZI1mI, where a freshly unmarshaled struct with an omitempty-d slice is nil.

The issue arises when encoding, not decoding (omitempty doesn't have any effect when decoding).

Specifically, it is sometimes useful to marshal a slice as [] even when it's nil (and also to be able to choose whether to include the field when the slice has zero length while retaining the ability to omit it)

Comment From: kylelemons

@rogpeppe re ptr naming

Thanks for pulling the data! I checked a few of the places that I use them, and they don't seem like they are likely to be a problem even if I were to allow the shadowing. It usually comes up for me when I am accepting an interface that I expect to be a pointer that is passed along to some reflect library as a way of "self documenting" the parameter's expectations. Something like:

func (r *R) Next(ptr interface{}) error {
  return r.jsonDecoder.Decode(ptr)
}

I don't see any of my examples both using ptr in this "documentation focused" way and still also needing to initialize a protobuf literal, which is where I think I would find myself using the proposed ptr function.

So, this is a long way of saying the name ptr would be fine with me if that's the direction this goes 👍.

Comment From: JAicewizard

I didnt fully follow the discussion, but I really feel like a function is a really non-go way to do this. We already have 2 ways of creating pointers, new and & (and maybe more). I really dont think we should add another way to do this. It may be very political to do it, no discussions about &int() vs &int{}, and simple to implement. However (in my opinion) it doesnt fit in with how go does things.

Comment From: davecheney

I stated a preference for the &int(n) form, but given this problem occurs when using encodings like gRPC, it seems like a simpler answer is for those projects to include a version of https://pkg.go.dev/go.uber.org/thriftrw/ptr for the small set of primitive types that don't support new or compact literal initialisation.

Comment From: thejerf

@JAicewizard, the root problem here is that there's two "how go does things" that are in some conflict here.

The obvious one is generalized Go simplicity, which leads to the &PrettyMuchAnythingHere syntax being preferred to consistently do whatever it takes to provide a pointer to whatever the result of the expression was.

The subtle one is that despite several syntax glosses that make it superficially act like a dynamic language that elides all allocations from the user, Go is explicit about allocations. It's easy to read := as a declaration, especially with it ignoring already-allocated values, but it's not just a declaration, it's an allocation.

Likewise, it's easy to see

for ... {
    var x int
}

as a "declaration" of an int variable in the for loop, but it's not; it's an allocation.

Letting & take a pointer to almost anything, regardless of whether what & is operating on is an allocation or not, further elides and complicates the question of "what is an allocation".

Given that all the technical solutions proposed to date in this thread are simple and of generally low consequences of them (inasmuch as any change at this level ever is), the real issue here is a philosophical one. Should Go continue being as explicit as it is about allocations (bearing in mind it's already not exactly 100%, but neither does it completely hide them from you), or should it move to elide yet more details about allocations in the interests of being a generally easier language?

Until that question is answered, the technical conversation here is going to go in circles, as the technical issues have largely been hashed out.

Comment From: JAicewizard

Allowing x := &int(55) or x := &int{55} will both cause as much allocation as x := &struct{a:55}. (that is, if x := &struct{a:55} wouldnt allocate neither would the first 2).

Since the latter is already allowed, the first 2 dont change much wrt the explicitness of allocations right?

I understand that trying to make everything &-able would indeed cause some problems, but I would argue that it would also make people write longer lines which is also (maybe implicitly) against the go principles.

And if we do want to have some generic function that copies and take the addres off something, that sounds very similar to the original proposal with new. only real diference I see is how the name

@thejerf Thank you for explaining it! these threads get quite long, and its hard to follow them sometimes.

Comment From: dolmen

In #46105 I propose a less concise syntax but that allows wider use cases, with a familiar syntax (think about ShortVarDecl used in if/switch):

p1 := (x := 3; &x)
p2 := (c := rune(10); &c)
p3 := (day := time.Tuesday; &day)
p4 := (unspec := Name("unspecified"); &unspec)

ptrTime := (t := time.Now(); &t)

Comment From: quenbyako

@dolmen p1 := (x := 3; &x)

yikes 😧

It looks like lambda functions, i don't think that this could be more useful then ambiguous in go...

Btw, i think this is overkill for this issue, f.e. p3 := (day := time.Tuesday; &day) can be replaced by p3 := &time.Tuesday already. so variants like p3 := &Name("unspecified") or ptrTime := &time.Now() looks more obvious especially for new gophers, imho

Comment From: dolmen

@quenbyako p3 := &time.Tuesday doesn't work today. My list of examples comes from the examples in Rob's proposal. Also &time.Now() is not covered by this proposal.

Comment From: justjoeyuk

Why is there so much complexity being thrown around? Can we not just literally bake some utilities into the core language?

pointers.String(mystring)
pointers.Int64(myint)
pointers.SomeOtherPrimitive(myotherprimitive)

The main issue I see here is tons of libraries creating their own functions for basic primitive conversions to pointers. Maybe that issue should be solved before exploring literally every other edge case imaginable. Creating basic utilities like this and baking them into the language stops immense amounts of repetition across a large number of hugely popular libraries.

Step One: Create utility functions for primitive types Step Two: Extend functionality to allow complex types using the new approach or whatever

At least doing the first step eliminates a massive annoyance currently rampant in many libraries. I'd refer again to https://github.com/AlekSi/pointer/issues/8

Comment From: icholy

@justjoeyuk https://www.youtube.com/watch?v=rFejpH_tAHM

Comment From: alnr

Another option to solve the ambiguity of

i := &3 // *int or *int64?

Could be to allow the syntax only when the compiler can infer the type:

var p *int32 = &3 // OK

func f(p *int64) {}
f(&58123) // OK

type S struct { p *int }
s := S{ p: &999 } // OK

x := &4412 // error: cannot infer type of x

var tooSmall *int8 = &1024 // error: literal 1024 overflows int8

Comment From: JAicewizard

the ambiguity already existed before this:

var x = 5 //what type is x?
fmt.Println(reflect.TypeOf(x)) // its an int!!!

I think adding the requirement of such explicit type indicators should be a separate proposal covering more than just this. that way it stays consistent, instead of needing it for pointers to ints, and not for other ints.

Comment From: clausecker

@alnr @JAicewizard I don't really see a problem with fixing the type of &foo where foo is an untyped integer constant to int. This is consistent with short variable declarations x := foo and an explicit type can always be given if desired.

Comment From: deanveloper

If we assign &5 to always be *int then you can’t do var x int64 = &5. Also, it may not be good to have &5 mean different things in different situations.

Currently we don’t encounter this with x := 5. 5 on its own is an untyped integer constant, and in x := 5, because x does not have a specified type, it is assigned an untyped integer constants default type, int.

This becomes much more complicated for &5, because we do not have a concept for “pointers to untyped integer constants”. So what would the &5 in x := &5 mean? How do we resolve it in a way that works for both x := &5 and var x *int64 = &5?

The solution provided by @clausecker only works for x := &5 but would fail to compile for var x *int64 = &5.

Comment From: clausecker

@deanveloper For a 64 bit int, you'd have to add a cast: x := &int64(5). This doesn't strike me as unreasonable.

Comment From: deanveloper

@clausecker I guess that works but it may be confusing, especially for a beginner. f(5) works, why can’t fPtr(&5)? (where the functions take an int64 and *int64 respectively)

Comment From: clausecker

@deanveloper While I acknowledge that this is less elegant than it could be, I don't think this is a problem that crops up too often and especially not for beginners. So no point in optimising for the rare case.

Btw, my proposal for this thing is to just allow building composite literals of scalars and have them be lvalues distinct from the initialiser. So you would have to write something like fPtr(&int64{5}) regardless of what type you want to use. One major advantage is that this syntax would not come with an implicit type cast and is thus safer against accidental type confusion.

Comment From: deanveloper

I’m personally not a fan of it because int64 isn’t a composite type, so composite literal syntax doesn’t exactly make sense for it imo. However I see the attraction to it and wouldn’t mind it per se. However then you get into semantics of what int64{…} means because int64 is not a composite type. For instance, would I be able to do int64{dur} where dur is time.Duration? It would add an entirely new concept that seems different from type conversion, but isn’t quite the same, which is what I would like to avoid in Go. I personally think that simply allowing pointers to typed constants (a la x := &int64(5)) is the best solution.

Comment From: quenbyako

@justjoeyuk Can we not just literally bake some utilities

No, we can't. If you so want to solve all the problems in the world by creating separate libraries for each one, I suggest you use Lua, there even the standard library consists of 5 (if memory serves me) packages. And this despite the fact that on the basis of Lua you can do everything the same as on go (yes, absolutely everything).

Get the problem right: the point is not that "this makes it possible to write less", but literally every second package is faced with the problem of creating pointers to simple types. Therefore, no, telling that "this is not a problem, just use pointers package" is not a solution to the problem.


@alnr // *int or *int64?

Huh? Of course it will be *int, cause every number without explicit type assertion became int. Am i worng?


@deanveloper i understand what you want to say, but i think, that this is not too bad idea to upgrade language parser... idk honestly, looks like you are right if there will be no changes to language specification

Comment From: DmitriyMV

No, we can't.

Yes we can. Generic pointer.Of(t T) *T solves this rather nicely.

cause every number without explicit type assertion became int

Not really - 10.0 becomes float64. Pointer expressions would also require to adjust language spec with something like pointer to untyped constants which is another can of worms.

Comment From: quenbyako

@DmitriyMV you didn't get the point of my comment:

Generic pointer.Of(t T) *T solves this rather nicely.

Nuh-uh, the point of "why we can't" is not about it's now technically impossible (of course it is possible, there is a pointers package, go on, use it). The point is to avoid one of the most frequent crutches in literally any go projects and just improve language specification. Yes, the use of pointers package is a crutch, for some reason the structures can be initialized immediately, and simple types suddenly can not.

10.0 becomes float64.

True, but, again, you didn't get the point of my comment:

i := 123             // reflect.TypeOf(i) == "int"
var j = 123          // reflect.TypeOf(j) == "int"
var k int64 = 123    // reflect.TypeOf(k) == "int64"
ip := &123           // with this proposal: reflect.TypeOf(i) == "*int"
var jp = &123        // with this proposal: reflect.TypeOf(i) == "int"
var kp &int64 = &123 // with this proposal, guess what?: reflect.TypeOf(k) == "int64"

f := 10.0             // float64
var g float32 = 10    // float32
fip := &10            // *int
ffp := &10.0          // *float64
var gp *float32 = &10 // *float32

Comment From: deanveloper

Yes we can. Generic pointer.Of(t T) *T solves this rather nicely.

it’s not really a great solution, as (it i remember correctly) return types cannot be inferred. this means it never really solves the f(&x) (where x is const and &x becomes *int64) problem. We’d need to specify the type (ie f(pointer.Of(int64(x))) or f(pointer.Of[int64](x))) which defeats the point of using generics to reduce verbosity.

either way, since the language allows pointers to composite literals, it seems to make sense to allow pointers to typed constants (ie &int64(x)).

my only issue with allowing pointers to typed constants is something like: const x int = 5; (…) &x

this makes it look like a pointer to x, however it’s actually a new pointer. a bit confusing, but i think its a good idea.

Comment From: DmitriyMV

@quenbyako

Yes, the use of pointers package is a crutch

With something like this:

package main

import (
    "fmt"
    . "pointers"
)


func main() {
    fmt.Println(PtrOf(10))
    fmt.Println(PtrOf(10.0))
}

it's not. Or you could use an alias.

for some reason the structures can be initialized immediately, and simple types suddenly can not.

  1. Structures are composite types.
  2. Address of struct doesn't do full type inference. That is - you cannot write var l = {Field1: 0}. Either you have a fully specified type on the left side or on the right.

you didn't get the point of my comment

You didn't get mine either. Try to describe "type of pointer to untyped constant" in formal terms (this is spec change we are talking about).

@deanveloper

as (it i remember correctly) return types cannot be inferred

They can: https://go2goplay.golang.org/p/SDLKf5rzosf

it seems to make sense to allow pointers to typed constants (ie &int64(x)).

That's sounds reasonable. Just like new(int, 3), though it looks a lot like new([]int, 3). I'm however strongly against something like i := &3.

Comment From: deanveloper

They can: https://go2goplay.golang.org/p/SDLKf5rzosf

That’s an incorrect use of what I meant. In that case, Pointer is returning to an interface{}. A more accurate example is https://go2goplay.golang.org/p/bIGdW1JDioN where Pointer returns to an int64.

Comment From: DmitriyMV

A more accurate example is https://go2goplay.golang.org/p/bIGdW1JDioN where Pointer returns to an int64.

I think it falls down to return type inferring from usage and that isn't planned ATM. Although maybe it's a good thing since in that case p := Pointer(3) would essentially be p := &3 and not p := &int(3)?

Comment From: deanveloper

I think it falls down to return type inferring from usage and that isn't planned ATM

yes, that is what i was trying to say

Although maybe it's a good thing since in that case p := Pointer(3) would essentially be p := &3 and not p := &int(3)?

my point essentially was that using a generic function is a bit clunky, and is a replacement for a feature that seems like it should be in the language in the first place (given that you can make inline pointers to composite literals, it seems strange you can’t do it for other expressions)

Comment From: quenbyako

@DmitriyMV . "pointers"

Please, never ever do this. I'm begging you (and any gopher), do not.

Btw, something like new(int, 3) doesn't look so scary as previous alternatives, so may it be a good design proposal

Comment From: benhoyt

Looks like this was last discussed in the proposal review meeting on May 5. While there's no clear consensus here, there are a number of good options. It seems like there's a fair bit of enthusiasm for Russ's simple new(1) form, and a decent amount of support for a new builtin generic function like Roger Peppe's ptr(1) suggestion. My vote would be for ptr(1) as it just uses "ordinary" generics, but I like new(1) too. Could this be discussed at the review meetings again?

Comment From: seebs

So, I was originally in favor of things like &expr but on reflection I slightly dislike the general case of that being allocatey, especially because it creates an ambiguity: given m[k], is p := &m[k] now equivalent to v := m[k]; p := &v? Because that seems like it loses a thing that currently prevents a significant error.

On the other hand, I don't really object to allowing composite literals for simple types. So, int(3) is an expression of type int with the value 3, and not addressable, but &int{3} would be declaring an object of type int, with the initializer 3, and taking its address. This seems consistent with the &foo.

And I think... if you accept the premise that automatically creating objects for expressions in general is Probably A Bad Idea, then I think there's a good case for a distinction between expressions and brace-form literals, where the latter are expressing the idea of "an object with these properties" rather than just "this value".

And I think this is an argument for not allowing, generically, taking the address of function results or map lookups or other things, because I think it's beneficial that &m[k] does not work. Since it can't give you the actual current address of m[k], it's pretty important that it not, instead, give you the address of an object which happens to look like m[k] did when you requested it.

But if you had to write &vtype{m[k]}, you'd be clearly indicating that you were not getting the address of m[k], you were getting the address of an anonymous literal.

I don't object to also the new(1) thing, I just think that the braced-literal syntax is nicer, and being able to do it for a handful of additional types would be a nice change.

Comment From: earthboundkid

Anyone will be able to write their own func NewOf[T any](v T) *T when Go 1.18 comes out. I say, let people play with that, and if it’s widely enjoyed and accepted, add a built in newof in 1.19.

Comment From: moonchant12

If this proposal is to resolve inconsistency, shouldn't it allow taking the address of functions as well?

As in,

fp := &func(){}

Comment From: earthboundkid

Here’s new.Of() for whoever wants it: https://github.com/carlmjohnson/new

Comment From: rogpeppe

Here’s new.Of() for whoever wants it: https://github.com/carlmjohnson/new

Personally I'd say that that package is a great illustration of the proverb "a little copying is better than a little dependency".

FWIW I've been just writing out this code wherever I need it:

func ref[T any](x T) *T {
    return &x
}

Comment From: earthboundkid

Personally I'd say that that package is a great illustration of the proverb "a little copying is better than a little dependency".

Well sure, but then what about my plan to replace it with a version that prints "Send carlmjohnson a fiver, will ya?" once it gets popular?

More seriously, ref is also a good name for the function.

Comment From: acehow

already 7 years passed since #9097 .so maybe go team will need another 7 years to support this change.

Comment From: quenbyako

@acehow instead of scoffing, you could help with the implementation. this is not an easy task as it might seem, you can add syntax sugar, but it will become really slow.

It's not very wise to scoff maintainers, when you have a super-powerful language literally for free.

And also, there is already good solution for this issue, so you don't need to wait new go version to handle cases like this))))

Comment From: afa7789

package types

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

When I want to make a quick reference Pointer, I have to use types.Ptr( VARIABLE ) , I guess this could be a casting tool native to go itself. Is something simple, usefull etc.

Like we havelen( VARIABLE ) ,print( stuff)why not pointer( VARIABLE ) or even ptr( VARIABLE ).

Anyway, I to used &[]string{VARIABLE}[0]to quick access pointer, and I guess it's something usefull.

It's really a great way to get it fast when you have to use it in another function

example.FunctionReceivePoitnerAndString(types.Ptr(Something),string1)so you don't have to create the Something pointer out of the scope.

edit: "I am now seeing everyone is proposing the same, shoudl have read it all srry guys"

Comment From: afa7789

Anyone will be able to write their own func NewOf[T any](v T) *T when Go 1.18 comes out. I say, let people play with that, and if it’s widely enjoyed and accepted, add a built in newof in 1.19.

awesome.

Comment From: earthboundkid

I think it's a useful datapoint that this is being independently recreated. :-)

Comment From: EraYaN

K8s has shipped pointer tooling for quite a while now in the k8s.io/utils/pointer package because you need it pretty darn often. At some point it might be worth it to have a look at it for the language itself.

Comment From: DeedleFake

@EraYaN

Something like that in the standard library definitely feels like overkill post-generics. As mentioned in previous comments, all it takes now is just one definition of

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

somewhere and it covers every single one of those to-pointer functions in a single function definition.

Comment From: EraYaN

But isn't something simple like that exactly something that ought to go into a stdlib? I get that golang is sort of weird about it's stdlib but still, seem like a core function to me.

Comment From: itroot

But isn't something simple like that exactly something that ought to go into a stdlib? I get that golang is sort of weird about it's stdlib but still, seem like a core function to me.

There are quite a few libs that have similar funcs; i.e.: https://github.com/samber/lo#toptr

Comment From: ivalue2333

But isn't something simple like that exactly something that ought to go into a stdlib? I get that golang is sort of weird about it's stdlib but still, seem like a core function to me.

add it to stdlib will be helpful, we have so many ptr usage in our project. And I hope this issue keep active and be solved

Comment From: ianlancetaylor

At this point the most viable options seem to be:

  • A new generic function somewhere in the standard library func PtrTo[T any](v T) *T { return &v }
  • &T(v) to get the address of a newly allocated variable of type T with the value v
  • new(v) which acts like PtrTo(v) (or perhaps new(T, v), or perhaps permit both forms)

Several other ideas have been mentioned but seem strictly worse than these.

@griesemer. @bradfitz, and @ianlancetaylor prefer permitting both new(v) and new(T, v). Permitting the latter form is similar to the way we permit both var x = v and var x T = v; this leaves it to the programmer to write clear code. (Either way, another possibility is var x = T(v) and new(T(v)), which works but is a bit more cluttered).

The disadvantage of PtrTo is simply the name: where in the standard library would that function go? The disadvantage of &T(v) is the similarity to composite literals and the possible confusion of when & allocates and when it takes the address of an existing variable.

While there are no immediate plans for any change here, are there strong objections to the choice of new(v) and new(T, V)? Thanks.

Comment From: benhoyt

@ianlancetaylor Thanks for getting back to this! Can you clarify which of the three choices you're referring to in "are there strong objections to that choice?" It's a bit ambiguous at present.

I slightly prefer new(v) over &T(v) because it eliminates stuttering in cases like new(time.Now()) -- that would be &time.Time(time.Now()) with the other syntax. If new(T, v) is supported in addition for clarity in certain cases, that's fine. new() is also a bit clearer that it always creates a "new" thing.

The other disadvantage of ptr.PtrTo is that it requires an import (and the package prefix on each use). Not a big deal, but just a slight irritation over new being always available and very short.

Comment From: griesemer

@benhoyt Ian meant the last of the three most viable options: are there strong objections to new(V) and new(T, v) which would act like PtrTo(v) (or PtrTo[T](v), respectively).

Comment From: ianlancetaylor

Edited my comment for clarity. Thanks.

Comment From: willfaught

p := &3

but that has the nasty problem that 3 does not have a type, so that just won't work.

3 can have a type: the default type for integer literals, int. 3.0 is float64, "3" is string, etc.

Instead, we define that conversions (and perhaps type assertions, but let's not worry about them here) are addressible.

Why not make any expression addressable if it's used like it's addressable?

That of course doesn't add much, and the stuttering is annoying, but it enables this form, making a number of previously clumsy pointer builds easy:

p1 := new(int, 3)

I don't see why the type argument is needed here. If it's a literal, use the default type. If not, use the actual type. new(3) could mean new(int(3)). new(f()) could use the result type of f.

Making type conversions addressable as a special case is inconsistent and magical. There's no rhyme or reason for it. It would be one of those magic incantations that new Go users have to memorize, with the help of the arcane knowledge of this GitHub issue explaining why it exists the way it does. If we wrap addressability into this, I think we should instead focus on making addressability more generalized/expressive so we can accomplish what this issue is about. Otherwise, we shouldn't involve addressability at all.

Comment From: DeedleFake

@benhoyt

new(time.Now()) reads really strangely to me. I'd much prefer &time.Now().

I wrote up a whole post about it but @willfaught beat me to it right above. My thought process is that

return &Struct{}

is conceptually equivalent to

s := Struct{}
return &s

so why not just expand that to every single-valued expression? It's always confused me that struct, array, slice, and map types are special in that regard. Just let me omit the intermediate variable for everything, be it &3, &"example", or &f().

Side note: Huh, maps can be pointer initialized. &map[int]int{1: 3, 2: 2, 3: 5} is completely valid. I had no idea before writing this.

Comment From: benhoyt

@willfaught It looks like you're responding to the original proposal at the top, rather than Ian's recent question. Which is fine, but several of your points have already been addressed. In particular, you said of new(int, 3) "I don't see why the type argument is needed here" -- but Ian's proposal is to allow new(3) (in addition to new(int, 3)).

@DeedleFake I actually created issue 22647 a while back proposing to allow the &anyExpression syntax, but I've been convinced away from that. As noted above and in Ian's comment on issue 9097, it's not quite "conceptually equivalent". When you do &Struct{} Go creates a new value every time and returns its address, but when you do &s Go returns the address of that same variable each time. Also, you could then write &myMap[k], which would make it look like map entries are addressable, but they're not (that construct is now an error). As such, I think allowing &anyExpression is more confusing than it's worth.

Comment From: zephyrtronium

Does the proposed new(v) infer the correct type when v is an untyped literal and the expression is used in a context requiring a defined type? That is, does this type check?

type MyString string

func F(*MyString) {}

func main() {
    F(new(""))
}

Comment From: willfaught

In particular, you said of new(int, 3) "I don't see why the type argument is needed here" -- but Ian's proposal is to allow new(3) (in addition to new(int, 3)).

@benhoyt I know, my objection was for including new(int, 3) because the type argument is redundant. new(3) is fine. Part of why var t T = x is allowed is because the assignment might involve conversion/assignability; another part is documentation. There's no such need in expressions for allocations.

Actually, his proposal was for new(v), not new(3), so as far as I can tell, my point about default types still stands.

Comment From: ianlancetaylor

In a case like new(3) or new("") the untyped constant will be given the default type, just as though you wrote var x1 = 3 or var x2 = "".

The question about F(new("")) is an interesting one. That would not type-check today, as we don't carry inferred types into function calls, only out of them. It's similar to

type MyString string

func F(*MyString) {}

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

func main() {
    F(PtrTo(""))
}

Today that fails to compile with

foo.go:10:7: cannot use PtrTo("") (value of type *string) as *MyString value in argument to F

In principle we could infer that this must be PtrTo[MyString] in order to satisfy the call to F, and that therefore the untyped string should be treated as MyString. But we don't make that kind of type inference today, and we have no plans to do so.

Comment From: rogpeppe

My main reservation about new(v) is that, as far as I know, all built-in functions take either a type or a value for a given argument - that is, for a given primitive, we always know which of the two we're expecting.

new(v) would blur that line.

Even though both type expressions and value expressions exist in the same namespace so there's no problem in principle with doing this, it makes me uneasy.

It also makes tooling a little less capable, because when I write new(, my editor can no longer know that I'm going to write a type and make appropriate suggestions.

My personal vote would be for a new built-in function (ref, ptr, newof... ?) which avoids the need for this argument kind ambiguity.

Comment From: robpike

That issue can be avoided if the type is required to be there, which was the original proposal. I believe this facility will be used relatively rarely, and the requirement to put the type there adds little pain overall while maintaining consistency.

Comment From: leighmcculloch

At this point the most viable options seem to be:

The three options don't seem viable to me, as described:


  • A new generic function somewhere in the standard library func PtrTo[T any](v T) *T { return &v }

Experimenting with this option, a generic function that copies, allocates, and returns the pointer, seems undesirable than any of the other options, because it causes the value to always be allocated on the heap, where-as other options discussed would result in the value appearing on either the stack or heap depending on context. (Assuming the other proposals follow the existing behavior of allocation for new and &S{...}.)


  • &T(v) to get the address of a newly allocated variable of type T with the value v

Breaking down &T(v) and reading it right-to-left it reads like: 1. v given some value 2. T(...) convert the value into T 3. & get the address of

Part 2 seems surprising. If v is already of type T it looks like the conversion shouldn't be necessary. & already means get the address of. T(...) already means convert a value to type T. It's surprising, and unintuitive, that combining the two means something new: "create a new value of T in a temporary and get the address of it." While the original proposal highlights that type conversion always creates a new value, this is not intuitive. When I see a type conversion, I don't see an intent to create a new value, I see only an intent to convert, so I don't think &T(v) communicates intent clearly enough.


are there strong objections to the choice of new(v) and new(T, V)?

I am not fond of new(v) because it will cause the new function's first parameter to be either a value or a type. That seems surprising given overloading functions isn't supported by Go.

I am not fond of only adding new(T, v) because it is verbose and introduces type stutter when used with structs. We already have verbose solutions to this problem (i.e. var v = 3; var p = &v;) and this proposal is a bigger win if the result is clear and succinct.

I am also not found of only adding new(T, v) because much application code I see in Go favor &S{...} over new. Creating a new way to do the same thing, when that new way is more verbose, will make it harder for new developers to learn Go. new(T, v) won't address the confusion new developers experience when &S{...} works, but &3 does not.

@robpike's original proposal included a shorthand where &v would be equivalent to new(T, v). new(T, v) seems pretty unattractive without that shorthand:

That is,

p := &expression

where expr is not an existing memory location is now just defined to be shorthand for

p := new(typeOfExpression, expression)

Even with the downsides of &expression that have been stated in comments, it seems like the easiest to describe to a new Go developer. I think it will be easier to explain that &v creates a temporary and gets that temporaries address in situations where the expression is not addressable, such as constants and function return values. The cases that the & shorthand is allowed could be restricted to disallow the most confusing case, such as accessing a map with &myMap[k].

If new(T, v) is to be added, it would be ideal to also add the shorthand &v for the constant and function return value non-addressable cases of v. It's pretty clear that &3 and &f() are addresses of temporaries/new values.

Comment From: earthboundkid

Experimenting with this option, a generic function that copies, allocates, and returns the pointer, seems undesirable than any of the other options, because it causes the value to always be allocated on the heap, where-as other options discussed would result in the value appearing on either the stack or heap depending on context.

I would assume that this is just a temporary compiler hiccough as generics are integrated in. I wouldn't make a long term decision based on it.

Comment From: DmitriyMV

@leighmcculloch

The results (as of 1.20.5) for me:

Benchmark1
Benchmark1-10       1000000000           0.9498 ns/op          0 B/op          0 allocs/op
Benchmark2a
Benchmark2a-10      1000000000           0.9457 ns/op          0 B/op          0 allocs/op
Benchmark2b
Benchmark2b-10      1000000000           0.9585 ns/op          0 B/op          0 allocs/op
Benchmark3
Benchmark3-10       1000000000           0.9427 ns/op          0 B/op          0 allocs/op
Benchmark4
Benchmark4-10       1000000000           0.9438 ns/op          0 B/op          0 allocs/op

No allocations.

Comment From: ianlancetaylor

Personally I think the very different behavior between &2 and &v (where v is a variable) means that we should not add &2 to the language. (The composite literal syntax is sufficiently different from &v that confusion doesn't seem likely.)

Comment From: leighmcculloch

the very different behavior between &2 and &v (where v is a variable) means that we should not add &2 to the language. (The composite literal syntax is sufficiently different from &v that confusion doesn't seem likely.)

From a Go user perspective I don't think the underlying behavior difference becomes a behavior difference in the program for constants and functions in a way that a developer could observe the behavior in a surprising way.

@ianlancetaylor Do we have an example where &2 would create application behavior that is confusing?

For constants there is no address that a developer could expect to receive. &2 would always give a new address, which is different to &v, but I haven't seen complaints of people being surprised by this for &S{...}, so that seems like a non-issue. (eg)

For function return I think the same is true. The example shared up in the thread (https://github.com/golang/go/issues/45624#issuecomment-822426632) for how function returns could be confusing is itself a confusing code sample already requiring an understanding of non-trivial concepts.

I understand there is confusion with maps, but use with map lookups (&myMap[...]) could be disallowed. (If maps ever got methods, &myMap.get(...) would be usable and less confusing.)

Comment From: DeedleFake

Personally I think the very different behavior between &2 and &v (where v is a variable) means that we should not add &2 to the language. (The composite literal syntax is sufficiently different from &v that confusion doesn't seem likely.)

What if, not including the existing composite literally syntax and possibly a new ability to address a return from a function, all allocating address usages require an extra set of parentheses, i.e. &(3) or &(someMap["key"])? ~~That's not legal currently and~~ seems sufficiently different to me to be obvious.

Edit: It is apparently currently legal. Huh. Well, either way, it may still work as a differentiator.

Comment From: zephyrtronium

@DeedleFake That is indeed legal currently: https://go.dev/play/p/m0YPHwq7CpR

Comment From: zephyrtronium

@leighmcculloch

From a Go user perspective I don't think the underlying behavior difference becomes a behavior difference in the program for constants and functions in a way that a developer could observe the behavior in a surprising way.

The difference between obtaining a pointer to an existing object (which necessarily aliases) and allocating a new object (which necessarily never aliases) is definitely a behavior difference and definitely easy to observe. Whether it's "in a surprising way" depends on what any given programmer expects. If a learning Go user is accustomed to &expr() never aliasing, and then it does when they do &variable or &slice[x], then it's different in a surprising way.

For constants there is no address that a developer could expect to receive. &2 would always give a new address, which is different to &v, but I haven't seen complaints of people being surprised by this for &S{...}, so that seems like a non-issue. (eg)

FWIW, I am actually surprised that this program prints false. If you modify it slightly, it doesn't: https://go.dev/play/p/UcHpIBuuoge. The Go spec says, "Pointers to distinct zero-size variables may or may not be equal." I have explained this to people who were surprised by it before.

I understand there is confusion with maps, but use with map lookups (&myMap[...]) could be disallowed. (If maps ever got methods, &myMap.get(...) would be usable and less confusing.)

I don't think that replacing one special case (the ability to take the address of composite literals) with another (only map index expressions cannot be the operand of unary &) makes the language less confusing. Maybe it doesn't make it more confusing, but I don't think "not worse" is a sufficient bar for language changes.

It also isn't the only kind of expression that could be confusing. Type assertions have already been mentioned: &anyVar.(int) wouldn't take the address of an int in an any, it would allocate a copy of the dynamic value of the int. I have also explained to people, generally looking to call pointer methods on non-pointer dynamic values, that you can't take the address of the value in an interface; having it "work" but operate on a copy might have been surprising to them.

Comment From: ianlancetaylor

Do we have an example where &2 would create application behavior that is confusing?

How about &syscall.ImplementsGetwd as compared to &syscall.ForkLock?

Comment From: clausecker

Let's extend the semantics of the unary + operator to apply to operands of any type, having the semantic of “turn lvalue into rvalue.” Then &+foo unambiguously points to a copy of foo.

Comment From: ianlancetaylor

Several people (@rogpeppe, @robpike and others) have commented that rather than both new(T, v) and new(v), we should only have new(T, v). That means that the first argument to new is always a type.

It's true that this makes the expression more verbose in some cases. However, it seems reasonable to guess that most cases where a long type is used are structs, and for which we have the &S{} literal notation. It seems less likely that people will want to write new(T, v) with a simple v and a complicated T. At least, it would be interesting to see specific examples where that comes up.

new(T, v) also matches make(T, length) syntactically, although the meaning is different.

As discussed above, there are several reasons why using an & syntax seems potentially confusing. The new(T, v) syntax should be fairly clear and never confusing.

Comment From: rogpeppe

@ianlancetaylor That's a slight mischaracterisation of my comment. To repeat, my preference is still to choose a new spelling for a function takes a value only. I'm not keen on shoehorning the functionality into new.

Comment From: Merovius

I don't particularly like new(T, v) either. I think the similarity to make detracts from its appeal, instead of adding to it: There already is some confusion of why make is used for some types but new (or &T{}) used for others. I also don't think it really applies - in make(T, v), the v means something completely different from what it'd mean in new(T, v). Lastly, in my opinion the extra overhead of typing out the T is significant in many cases, where the value you want it initialized to can be inferred. new(int64(42)) isn't any more to type or read than new(int64, 42), but new(time.Second) is significantly better than new(time.Duration, time.Second). I don't think having the type in there really adds anything. We are already kind of used to inferring the type from a constant literal.

That being said, new(T, v) is still better than where we're at, so if we can't agree on new(v) and if we can't agree on a better name for pointerTo(v), then I'd live with new(T, v).

Comment From: icholy

but new(time.Second) is significantly better than new(time.Duration, time.Second).

It's better if I already know that time.Second is value. But if you're reading new(pkg.Ident), there's no way to tell which overload you're using without checking the definition of pkg.Ident.

Comment From: rogpeppe

Another point: the new(T, v) form is also inconvenient in the not-uncommon case where we want to make a copy of a pointer type.

e.g.

func f(x Foo) {
   x.field = new(int, *x.field)   // where int is the type of Foo.field
   // use x.field without worrying about shared pointers.
}

We have to mention the type where there's otherwise no need for it and it might not be obvious, making the code a little more brittle.

With a "pointerTo"-style function, it's nicer IMHO:

func f(x Foo) {
   x.field = ref(x.field)
   // use x.field without worrying about shared pointers.
}    

// ... for some spelling of "ref"
func ref[T any](x T) *T { return &x }

Comment From: earthboundkid

I don’t like the new(v) form because it depends on the reader knowing if v is a value or a type. Between new(T, v) and ref(v), I could live with either, and it really just depends on if you think it’s better to not add another predeclared identifier or better to not add an overloaded form for an existing identifier.

Comment From: ianlancetaylor

@rogpeppe Apologies for the mischaracterization.

Comment From: Merovius

FWIW I agree that new(X) is ambiguous because it requires to know if X is a value or a type. I just used that to make the point that I don't like new(T, v). I think my favorite version would be ptr(v), but I assume ptr is not an acceptable name. Someone might have a better one.

But as I said, if we can't agree on any color of bikeshed for a type-argument-less version of this builtin, new(T, v) is still better than nothing.

Comment From: DeedleFake

If new(T, v) is the form that gets adopted, I will probably just find myself writing

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

in random places anyways to avoid the hassle, though admittedly less often as new(T, v) will be useful sometimes. Overall I think overloading new() is the wrong way to go, especially if it's going to force it into a significantly less helpful syntax.

Comment From: willfaught

We could lean into the syntax we already have:

&&value
&&time.Second
var p *int = &&123

Comment From: jimmyfrasche

One of the subproposals discussed in #34515 is to omit the type in make/new whenever you could omit the type in a call to a generic function. new(Type, value) would likely take that option off the table as that brings it back to new(value).

Comment From: metux

Bonus challenge: do the same for make :) I think it would boil down to extending the struct literal initialization syntax in some way that could cover just these 4 things:

apropos male():

Still havent understood why map fields always need to be explicitly initialized by make(), instead of directly working off the zero value. This makes using maps in structs much more complicated.

Comment From: DeedleFake

Every type's zero value is literally the memory all set to zero. A map is a pointer to a struct internally, so its zero value is literally just a nil pointer. Trying to make the zero value behave differently would fail in the following situation, among others:

func addThing(m map[string]string) {
  // This would allocate an hmap, but only this m would get set to its address.
  m["example"] = "This is an example."
}

func main() {
  // Remember, this is a *hmap.
  var m map[string]string

  // addThing() gets a copy of the address, currently nil.
  addThing(m)
  // No matter what addThing() does, the local m is still nil at this point.
}

Comment From: metux

Every type's zero value is literally the memory all set to zero. A map is a pointer to a struct internally, so its zero value is literally just a nil pointer. Trying to make the zero value behave differently would fail in the following situation, among others:

```go func addThing(m map[string]string) { // This would allocate an hmap, but only this m would get set to its address. m["example"] = "This is an example." }

Ah, so a map can be directly passed as value (instead of reference/pointer to it) while still using the same underlying hashtable. Good news to me, wasn't sure that's really the case :)

Indeed, now an on-demand allocation would cause this kind of trouble. If we'd ever go that way, this would need to be clearly documented and probably code checkers should look for bugs where the programmer might have forgotten it ... certainly not nice.

But what would happen (besides extra compiler complexity) if we'd let it implicitly emit an makemap() call, if it doesn't find another one (that probably asks for different initial size) ? IIRC, the worst that could happen (when there is an explicit make that goes unnoticed), we might have an wasted allocation or memory clear, but shouldn't hurt semantics at all.

Am I missing something ?

By the way, still haven't fully understood how code generation and runtime code really work together ... if a program doesn't use maps at all, does the deadcode elimination kick out all the map-related runtime code ?

thx.

Comment From: adonovan

Summarizing some comments: - &v is unclear as to whether it allocates, or returns an existing variable's address. - new(v) is concise but easily confused for new(T). - new(T, v) is clear and similar to make, but the type is redundant. - the function can be written as a one-liner: func addr[T any](v T) *T { return &v }

Given that it is trivial to write the helper function, a language change would add marginal value. @robpike, do you still think there's a need to do anything here?

Comment From: robpike

@adonovan There is certainly no need, but I still find the imbalance troubling: it's easier to build a pointer to a complex thing than to a simple one.

None of the bullet points in your list seem fatal to me. The first one is irrelevant to what I suggested, the middle two are true but not clearly problems, while the ease of writing that function doesn't touch the fundamental asymmetry.

Comment From: findleyr

Leaving open until someone brings this discussion to a consensus. — rfindley for the language proposal review group

Comment From: perj

the function can be written as a one-liner: func addrT any *T { return &v }

Judging from the past 1.5 years, I appear to be writing this function about once every second month, when I need it in a new package. The need especially arise with pointers to strings in unit test files, I've noticed.

Admittedly, I work a lot with code generated from API specifications. That code tend to use *string a lot, since it can't be certain that nil and empty string are equivalent (and rightly so, they aren't).

It's not very annoying, but does feel a bit like I'm littering my packages with this function, so not having to write it would be welcome. I do realise I can put it in a package I import, but that also seems overkill for a one-liner.

Comment From: smasher164

As far as being explicit about allocation is concerned, I think Go is well past that point. Whether or not an object is on the stack or the heap is entirely predicated by escape analysis. If anything, &v is more like ML's ref v which says "Hey here's a mutable reference to this value-whether it's on the stack or the heap is implementation-defined."

Comment From: andig

Given that it is trivial to write the helper function, a language change would add marginal value.

The helper function can easily be called with a pointer value which would typically be a programming error. Having a language construct could prevent this, either by rule or by removing a layer of indirection.

Comment From: earthboundkid

  • new(T, v) is clear and similar to make, but the type is redundant.

If the type inference worked well enough (perhaps by just hard coding this) that you could write new(T, { field1, field2 }) instead of new(T, T{ field1, field2 }), it wouldn't be so bad. The comma is weird, but it exists so you don't confuse T with v, which is good.

Comment From: bbkane

Has there been any more updates to this?

Similar to @perj , I've written this function for multiple modules, and I've seen it in 3rd party modules many times as well, often with different names.

In addition to the "symmetry" argument from @robpike , I want a standard way to create a pointer to a primitive type simply to get some standardization.

This issue has been open since 2021, and most commenters welcome the idea, though I haven't seen super strong arguments on either side for a winning syntax. Could someone on the language team pick a winner so I can start using it when it's implemented?

Comment From: adonovan

@robpike's original option 1, new(T, v) has a lot to recommend it. The type is explicit, and the allocation of a new variable is explicit, so it's almost impossible to misunderstand what it means. And it's a small language change, being merely the addition of a parameter to an existing function.

I'll bring it up again at the next review.

Comment From: robpike

Very happy to hear that. I care deeply about this wart (the asymmetry) in the language, although it's not a major flaw.

Comment From: mitar

My main use case is in JSON with optional fields where I have a struct with *string or *int types:

type Foo struct {
  String *string `json:"string,omitempty"`
  Int *int `json:"int,omitempty"`
}

Creating such a struct is currently really painful. But proposed syntax from option 1 is also very verbose:

Foo{
  String: new(string, "x"),
  Int: new(int, 0),
}

Could we maybe allow:

Foo{
  String: new("x"),
  Int: new(0),
}

Comment From: ianlancetaylor

@mitar There has been a lot of discussion on this issue. I believe that idea was first proposed at https://github.com/golang/go/issues/45624#issuecomment-822211103.

Comment From: ianlancetaylor

I believe my most recent summary of this issue was https://github.com/golang/go/issues/45624#issuecomment-1581546783 followed by https://github.com/golang/go/issues/45624#issuecomment-1642776089.

Comment From: ianlancetaylor

Moving to regular proposal committee.

Comment From: rogpeppe

@robpike's original option 1, new(T, v) has a lot to recommend it. The type is explicit, and the allocation of a new variable is explicit, so it's almost impossible to misunderstand what it means. And it's a small language change, being merely the addition of a parameter to an existing function.

FWIW I still write this tiny function wherever I need it:

func ref[T any](x T) *T {
    return &x
}

Replacing, for example, ref(someMap[x]) with new(SomeType, someMap[x]) would be a net loss because it makes the code more verbose and a little bit more fragile, requiring update should the type of the map's values change.

Another idiom I commonly use involving the above function is to make a shallow copy of a pointer value:

    x := ref(*y)

Again, the requirement to explicitly mention the type of y there is unnecessarily annoying, particularly for types with large names such as inline-defined structs.

I still think that some equivalent of the ref function above would be a nice addition to the language.

Comment From: t9t

I would like to add a point related to naming that I think has not been voiced in this thread (which I just fully read; I hope I didn't miss it).

There have been some proposals to add a new function, e.g. a built-in called ref or ptr, or in the standard library like pointer.Of.

Imagine the following hypothetical snippet:

a := 10
p := ptr(a)
// or:
r := ref(a)

My first thought when seeing this, is that ptr(a) or ref(a) is equivalent to &a, i.e. taking the address of (getting a pointer or reference to) the variable a. But it's not: &a will give the same address each time, while ptr(a)/ref(a) would make a copy, and return an address to the copy.

While I'm not a big fan of the new(T, v) syntax because it can become verbose in certain cases, I do like the name new here because it says exactly what it's doing: creating something new rather than pointing to (or referencing) something existing.

Comment From: earthboundkid

While I'm not a big fan of the new(T, v) syntax because it can become verbose in certain cases, I do like the name new here because it says exactly what it's doing: creating something new rather than pointing to (or referencing) something existing.

I like the name newof for similar reasons.

Comment From: adonovan

While I'm not a big fan of the new(T, v) syntax because it can become verbose in certain cases, I do like the name new here because it says exactly what it's doing: creating something new rather than pointing to (or referencing) something existing.

I like the name newof for similar reasons.

I wonder: would it be possible to change the interpretation of new so that it accepts its current form new(T) but also new forms such as new[T](), new[T](expr), and new(expr)? Given that the call and conversion forms f(x) and T(x) are not distinguishable by the parser, which must parse f as an expression and leave it to the type checker to discriminate types from terms, it should be possible for the type checker to interpret the first argument of new in a similar way.

Then we could use the style new(T) or new[T]() for calls that construct a zero-valued variable, and new(x) or new[T](x) when we need to initialize the variable, the latter case for when x is not a T.

Comment From: apparentlymart

The idea of overloading new so that its single argument can be either a type or a value was already discussed multiple times earlier in the thread, and already has some concerns raised that I'm linking here just in the hope that we don't need to repeat these same ideas again:

  • https://github.com/golang/go/issues/45624#issuecomment-1582248350
  • https://github.com/golang/go/issues/45624#issuecomment-1644290116
  • https://github.com/golang/go/issues/45624#issuecomment-1642776089

I'm sharing these links only to help find earlier discussion. I don't have a strong opinion on this subject.