https://github.com/golang/go/issues/45624 Skip to content Sign up Sign up * Why GitHub? Features - + Mobile - + Actions - + Codespaces - + Packages - + Security - + Code review - + Project management - + Integrations - + GitHub Sponsors - + Customer stories- * Team * Enterprise * Explore + Explore GitHub - Learn and contribute + Topics - + Collections - + Trending - + Learning Lab - + Open source guides - Connect with others + The ReadME Project - + Events - + Community forum - + GitHub Education - + GitHub Stars program - * Marketplace * Pricing Plans - + Compare plans - + Contact Sales - + Education - [ ] [search-key] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this organization All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up Sign up {{ message }} golang / go * Notifications * Star 84.7k * Fork 12.3k * Code * Issues 5k+ * Pull requests 281 * Actions * Projects 3 * Wiki * Security * Insights More * Code * Issues * Pull requests * Actions * Projects * Wiki * Security * Insights New issue Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community. Pick a username [ ] Email Address [ ] Password [ ] [ ] Sign up for GitHub By clicking "Sign up for GitHub", you agree to our terms of service and privacy statement. We'll occasionally send you account related emails. Already on GitHub? Sign in to your account Jump to bottom proposal: expression to create pointer to simple types #45624 Open robpike opened this issue Apr 19, 2021 * 45 comments Open proposal: expression to create pointer to simple types #45624 robpike opened this issue Apr 19, 2021 * 45 comments Labels Go2 LanguageChange Proposal Milestone Proposal Comments @robpike Copy link Contributor @robpike robpike commented Apr 19, 2021 * edited 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 The text was updated successfully, but these errors were encountered: 227 3 16 [?] 19 8 2 @gopherbot gopherbot added this to the Proposal milestone Apr 19, 2021 @gopherbot gopherbot added the Proposal label Apr 19, 2021 @seebs Copy link Contributor @seebs seebs commented Apr 19, 2021 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. 10 @seankhliao seankhliao added Go2 LanguageChange labels Apr 19, 2021 @clausecker Copy link @clausecker clausecker commented Apr 19, 2021 Alternatively, what's the problem with adding composite literals of simple types, like int{3}? 12 18 @JAicewizard Copy link @JAicewizard JAicewizard commented Apr 19, 2021 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. 1 @faiface Copy link @faiface faiface commented Apr 19, 2021 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. 43 1 @eaglebush Copy link @eaglebush eaglebush commented Apr 19, 2021 * edited 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() }()) @peterbourgon Copy link Member @peterbourgon peterbourgon commented Apr 19, 2021 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) 13 @benhoyt Copy link Contributor @benhoyt benhoyt commented Apr 19, 2021 @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. 5 @faiface Copy link @faiface faiface commented Apr 19, 2021 @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. @benhoyt Copy link Contributor @benhoyt benhoyt commented Apr 19, 2021 @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. @clausecker Copy link @clausecker clausecker commented Apr 19, 2021 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). 1 1 1 @clausecker Copy link @clausecker clausecker commented Apr 19, 2021 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. 1 @golang golang deleted a comment from opennota Apr 19, 2021 @mcandre Copy link @mcandre mcandre commented Apr 19, 2021 Rob, don't tell me about such a gap. I was implementing Bliss interface for so long. @robpike Copy link Contributor Author @robpike robpike commented Apr 19, 2021 * edited @clausecker Because why add a new construct (&int{3}) when you can use an existing one? 5 @clausecker Copy link @clausecker clausecker commented Apr 19, 2021 * edited @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. 2 1 @ninedraft Copy link @ninedraft ninedraft commented Apr 19, 2021 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) 3 @thejerf Copy link @thejerf thejerf commented Apr 19, 2021 * edited 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. 7 3 @zkosanovic Copy link @zkosanovic zkosanovic commented Apr 19, 2021 @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. 3 @smasher164 Copy link Member @smasher164 smasher164 commented Apr 19, 2021 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. 6 @sanggonlee Copy link @sanggonlee sanggonlee commented Apr 19, 2021 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. @sethvargo Copy link Contributor @sethvargo sethvargo commented Apr 19, 2021 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)). 8 @rh-kpatel4 Copy link @rh-kpatel4 rh-kpatel4 commented Apr 19, 2021 Why not &(int64(11))? This is proper scoping to take the output of () and return pointer to it &()? 2 2 @FiloSottile Copy link Contributor @FiloSottile FiloSottile commented Apr 19, 2021 What about generalizing the second approach and simply allow taking the address of a function result? 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. 7 @carlmjohnson Copy link Contributor @carlmjohnson carlmjohnson commented Apr 19, 2021 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. 3 @bcmills Copy link Member @bcmills bcmills commented Apr 19, 2021 * edited 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 (#45624 (comment)) 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. 6 @fkarakas Copy link @fkarakas fkarakas commented Apr 19, 2021 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.... 13 1 @clausecker Copy link @clausecker clausecker commented Apr 19, 2021 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? 8 @rsc Copy link Contributor @rsc rsc commented Apr 19, 2021 * edited 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{...}). 17 @rsc Copy link Contributor @rsc rsc commented Apr 19, 2021 @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. 15 [?] 2 @randall77 Copy link Contributor @randall77 randall77 commented Apr 19, 2021 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? 3 @rsc Copy link Contributor @rsc rsc commented Apr 19, 2021 * edited 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. 2 @Laremere Copy link @Laremere Laremere commented Apr 19, 2021 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? 6 @rsc Copy link Contributor @rsc rsc commented Apr 19, 2021 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. @seebs Copy link Contributor @seebs seebs commented Apr 19, 2021 * edited 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} 12 @xaionaro Copy link Contributor @xaionaro xaionaro commented Apr 19, 2021 * edited 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. 1 @seebs Copy link Contributor @seebs seebs commented Apr 19, 2021 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. @Laremere Copy link @Laremere Laremere commented Apr 19, 2021 @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. 2 1 @virus-found virus-found mentioned this issue Apr 19, 2021 Still can't get rid of unfurl wee-slack/wee-slack#834 Open @ianlancetaylor ianlancetaylor mentioned this issue Apr 19, 2021 proposal: spec: add &T(v) to allocate variable of type T, set to v, and return address #9097 Open @nemith Copy link Contributor @nemith nemith commented Apr 19, 2021 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. 3 @DeedleFake Copy link @DeedleFake DeedleFake commented Apr 19, 2021 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. 5 @slrz Copy link @slrz slrz commented Apr 19, 2021 The new extension looks very nice and clean. Probably worth it even without introducing the &expression shorthand for new (typeOfExpression, expression). @travisjeffery Copy link @travisjeffery travisjeffery commented Apr 19, 2021 * edited I prefer adding the new parameter. Looks a lot cleaner and simpler from a language standpoint by not overloading &. 2 @mdempsky Copy link Member @mdempsky mdempsky commented Apr 19, 2021 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. 2 @HALtheWise Copy link @HALtheWise HALtheWise commented Apr 19, 2021 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. @icholy Copy link @icholy icholy commented Apr 19, 2021 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. 1 @ajwerner ajwerner mentioned this issue Apr 19, 2021 proposal: Go 2: spec: generic parameterization of array sizes #44253 Open @smasher164 Copy link Member @smasher164 smasher164 commented Apr 19, 2021 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|...]. @ysmood Copy link @ysmood ysmood commented Apr 19, 2021 * edited How about: type Cube struct { Size int } a := new(Cube{Size: 10}) b := new(10) // by default it's type int var c int64 = new(10) // tell compile what type we want d := new("string") Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment Assignees No one assigned Labels Go2 LanguageChange Proposal Projects None yet Milestone Proposal Linked pull requests Successfully merging a pull request may close this issue. None yet 34 participants @mcandre @travisjeffery @mdempsky @rsc @peterbourgon @seebs @Laremere @carlmjohnson @thejerf @DeedleFake @clausecker @nemith @sethvargo @icholy @benhoyt @FiloSottile @ysmood @HALtheWise @xaionaro @faiface and others * (c) 2021 GitHub, Inc. * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time. You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.