Skip to content
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.

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

proposal: Go 2: make constants and literals addressable #28681

Closed
deanveloper opened this issue Nov 8, 2018 · 4 comments
Closed

proposal: Go 2: make constants and literals addressable #28681

deanveloper opened this issue Nov 8, 2018 · 4 comments

Comments

@deanveloper
Copy link

I couldn't find any proposals for this, but it seems like something that someone would have already proposed. So I apologize if this is a duplicate.

This proposal is to make constants, and some expressions, addressable. It is quite simple so I will keep it that way.

How it works

A pointer to a constant expression would result in a new pointer to the value the expression represents.

This is the same way that pointers to pointers to composite literals work, so I don't personally think it's unintuitive or anything. The only slightly unintuitive part is that &namedConstant == &namedConstant evaluates to false, although I don't personally find that unintuitive.

Example:

const hello = "hello"

fmt.Println(&346) // output: 0x416020

fmt.Println(&hello == &hello) // output: false

Modifying constant pointers

Modifying a constant pointer should not panic. The value at the pointer would still be modified, because the pointer doesn't really point to five, it points to a value that the calling function doesn't have access to anyway. This is exactly how pointers to composite literals work.

// Scanf would still modify a value, we just don't have access to it
fmt.Scanf("%d", &5)

// similarly, but perhaps less intuitively

const five = 5

// Scanf again still modifies a value, but we don't have access to it
fmt.Scanf("%d", &five)

fmt.Println(five) // output: 5

Modifying constant pointers (with read-only types)

If we had read-only types, the resulting pointer would be a read-only pointer. So ideally our fmt.Scanf lines from the previous example would not even compile.

@gopherbot gopherbot added this to the Proposal milestone Nov 8, 2018
@martisch
Copy link
Contributor

martisch commented Nov 8, 2018

  1. Can you give an existing code example where this would be helpful and why it does improve the code in the example?

  2. if each & gives a new memory address does that mean that each time an address of a constant is taken at runtime it needs to copy the constant into new memory and return a pointer? That would seem a lot of hidden cost behind & for constants being different then e.g. for variables. Which would raise the question for me what the advantage is over initializing a variable with the constant and then taking the address of the variable.

  3. Would the type of a pointer to a string constant be the same as the type of a pointer to a string?

@ALTree
Copy link
Member

ALTree commented Nov 8, 2018

This is #19966, which was closed as a special case of the more general #9097 (see e.g. #9097 (comment)).

@deanveloper
Copy link
Author

Can you give an existing code example where this would be helpful and why it does improve the code in the example?

My bad, I should have included real-world examples.

The main advantage is brevity. Especially when working with constructs that have nullables (ie JSON) structures need to have *string values rather than simply string.

*string is also used frequently in structs made for passing lots of parameters to a function. This makes it possible to tell the difference between explicitly providing an empty string and not including the argument.

Initializing these structs that use *string quickly becomes painful. For instance, with AWS:

// standard Go way without `aws.String` convenience function
var bucket = "MyBucket"
var key = "SomeFile.pdf"
downloader.DownloadWithContext(ctx, buffer, &s3.GetObjectInput{
	Bucket: &bucket,
	Key: &key
})

Now imagine calling that function but using all of the possible parameters

AWS realized that this was a problem, so they provided a "solution":

func String(str string) *string {
	return &str
}

So then our function call would look like:

downloader.DownloadWithContext(ctx, buffer, &s3.GetObjectInput{
	Bucket: aws.String("MyBucket"),
	Key: aws.String("key")
})

The repetition of aws.String is still quite painful. Again, think about needing to use that for every single AWS call.

if each & gives a new memory address does that mean that each time an address of a constant is taken at runtime it needs to copy the constant into new memory and return a pointer? That would seem a lot of hidden cost behind & for constants being different then e.g. for variables.

That is fair I guess. Having it shown over two lines is good to show that there is extra cost. I'd personally call that a micro-optimization though. After all &"hello" would be equivalent to hello := "hello"; &hello. I wouldn't exactly call that expensive. I really would think of it as more syntactic sugar. It would only be more expensive if you create lots of pointers to the same value.

Which would raise the question for me what the advantage is over initializing a variable with the constant and then taking the address of the variable.

Again, my AWS example is what I'm trying to avoid.

@deanveloper
Copy link
Author

This is #19966, which was closed as a special case of the more general #9097 (see e.g. #9097 (comment)).

My bad, you are correct. Closing this.

@golang golang locked and limited conversation to collaborators Nov 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants