53

We have written one program by which we try to find an address of a constant. Is it possible to do it like that?

package main

func main() {
        const k = 5
        address := &k
}

It gives an error, can anyone tell how can we find the address of a constant?

icza
  • 389,944
  • 63
  • 907
  • 827
Arvind
  • 2,671
  • 1
  • 18
  • 32

5 Answers5

77

In short: you can't.

The error message says:

cannot take the address of k

There are limitations on the operand of the address operator &. Spec: Address operators:

For an operand x of type T, the address operation &x generates a pointer of type *T to x. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal. If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.

Constants are not listed as addressable, and things that are not listed in the spec as addressable (quoted above) cannot be the operand of the address operator & (you can't take the address of them).

It is not allowed to take the address of a constant. This is for 2 reasons:

  1. A constant may not have an address at all.
  2. And even if a constant value is stored in memory at runtime, this is to help the runtime to keep constants that: constant. If you could take the address of a constant value, you could assign the address (pointer) to a variable and you could change that (the pointed value, the value of the constant). Robert Griesemer (one of Go's authors) wrote why it's not allowed to take a string literal's address: "If you could take the address of a string constant, you could call a function [that assigns to the pointed value resulting in] possibly strange effects - you certainly wouldn't want the literal string constant to change." (source)

If you need a pointer to a value being equal to that constant, assign it to a variable of which is addressable so you can take its address, e.g.

func main() {
    const k = 5
    v := k
    address := &v // This is allowed
}

But know that in Go numeric constants represent values of arbitrary precision and do not overflow. When you assign the value of a constant to a variable, it may not be possible (e.g. the constant may be greater than the max value of the variable's type you're assigning it to - resulting in compile-time error), or it may not be the same (e.g. in case of floating point constants, it may lose precision).

icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    so we can not find the address of constant :) – Arvind Feb 02 '16 at 07:09
  • At least the constant is constant, the world is safe :) – dSebastien Jul 14 '18 at 22:15
  • 2
    The second reason is wrong, unless Go intentionally supports runtimes with no MMU (in which case that should be said explicitly to make the reason not wrong): Preventing taking an address is not necessary to keep a value in memory constant - such constants could be stored in a memory page marked read-only. Every modern operating system running on hardware with an MMU can do this, and in fact does by default for many of the memory pages that the process starts with. – mtraceur Jan 29 '20 at 03:58
  • @mtraceur Whether it's possible to mark the memory read-only falls outside of Go's grammar. If you have a non-`nil` pointer and you assign something to the pointed value, you expect it to change. The spec does not allow "conditional success" of such assignment. – icza Jan 29 '20 at 08:23
  • @icza I don't see how that has anything to do with what I said. Where did you get anything about conditional success? Conditional success is not necessary for nor logically implied by anything I said, and I cannot even word how infuriatingly frustrating it is for me to communicate with someone who managed to think their way to this response to my comment, so I'm gonna just walk away from this until maybe one day I have the patience to try to clarify constructively. – mtraceur Jan 29 '20 at 09:42
  • Go's authors had other options, as language designers, to help the runtime keep constants constant. So it is wrong to just say it is a reason for doing so, because by itself that is not sufficient reason to do that, yet saying it like that implies that it logically is sufficient - but there must also be additional reasoning to prefer that to the other viable alternatives. That additional reasoning was probably that they prefered to not do any of the other alternatives because they would impose more complexity or special cases or {implementation ,un}defined behavior to the language. – mtraceur Jan 29 '20 at 10:15
  • @mtraceur Robert Griesemer (one of Go's authors) wrote why it's not allowed to take a string literal's address: "_If you could take the address of a string constant, you could call a function [that assigns to the pointed value resulting in] possibly strange effects - you certainly wouldn't want the literal string constant to change."_ [source](https://groups.google.com/d/msg/golang-nuts/mKJbGRRJm7c/K3k3x6-huw4J) – icza Jan 29 '20 at 10:34
  • @icza Thank you. I thought this might come up. His own reasoning would be wrong in the sense of "logically incomplete" **if** his reasoning is *completely* represented by that quote and didn't include other thinking about why to prefer the way of disallowing address-taking given the tradeoffs of other alternatives. It does mean that you would be correct to say that it is this way because one of the creators working on the language thought this. But personally I would explicitly indicate that by quoting and citing it to not implicitly endorse it as objectively sound reasoning *in isolation*. – mtraceur Jan 30 '20 at 00:10
41

I often hit this problem when creating large, nested JSON objects during unit tests. I might have a structure where all the fields are pointers to strings/ints:

type Obj struct {
    Prop1  *string
    Prop2  *int
    Status *string
}

and want to write something like:

obj := Obj{
    Prop1:  &"a string property",
    Prop2:  &5,
    Status: &statuses.Awesome,
}

When I initialise it, but the language doesn't allow this directly. A quick way to bypass this is to define a function that takes a constant and returns its address:

 s := func(s string) *string { return &s }
 i := func(i int) *int { return &i }

 obj := Obj{
    Prop1:  s("a string property"),
    Prop2:  i(5),
    Status: s(statuses.Awesome)
}

This works due to the fact that when the constant is passed as a parameter to the function, a copy of the constant is made which means the pointer created in the function does not point to the address of the constant, but to the address of its copy, in the same way as when a constant value is assigned to a var. However, using a function to do this makes it more readable/less cumbersome IMO than having to forward declare large blocks of variables.

The AWS SDK uses this technique. I now find myself regularly adding a package to my projects that looks something like:

package ref

import "time"

func Bool(i bool) *bool {
    return &i
}

func Int(i int) *int {
    return &i
}

func Int64(i int64) *int64 {
    return &i
}

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

func Duration(i time.Duration) *time.Duration {
    return &i
}

func Strings(ss []string) []*string {
    r := make([]*string, len(ss))
    for i := range ss {
        r[i] = &ss[i]
    }
    return r
}

Which I call in the following way:

func (t: Target) assignString(to string, value string) {
    if to == tags.AuthorityId {
        t.authorityId = ref.String(value)
    }
    // ...
}

You can also add a deref package, though I have generally found this to be less useful:

package deref

func String(s *string, d string) string {
    if s != nil { return *s }
    return d
}

// more derefs here.

EDIT April 2022:

With the release of go 1.18, it's now possible to define a single method to handle all conversions from constants into pointers:

package ref

func Of[E any](e E) *E {
    return &e
}
Orphid
  • 2,722
  • 2
  • 27
  • 41
2

These 3 options could be helpful:

  1. Using a helper function with generics. (Works for both primitive and custom types)
package main

import "fmt"

type Role string

const (
    Engineer  Role = "ENGINEER"
    Architect Role = "ARCHITECT"
)

const (
    EngineerStr  string = "ENGINEER"
    ArchitectStr string = "ARCHITECT"
)

func main() {
    fmt.Println(PointerTo(Engineer))    // works for custom types
    fmt.Println(PointerTo(EngineerStr)) // works for primitive types
}

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

Try it on playground

  1. Using pointy. (Works only for primitive types)

  2. Using a ToPointer() method. (Works only for custom types)

package main

import "fmt"

type Role string

const (
    Engineer  Role = "ENGINEER"
    Architect Role = "ARCHITECT"
)

func (r Role) ToPointer() *Role {
    return &r
}

func main() {
    fmt.Println(Engineer.ToPointer())
}

Try it on playground

Said Saifi
  • 1,995
  • 7
  • 26
  • 45
1

I found another way to deal with this, which is using AWS API:

import "github.com/aws/aws-sdk-go/aws"

type Obj struct {
    *int
}

x := aws.Int(16) // return address
obj := Obj{x} // work fine

this method is literally same as the answer above, but you dont have to write the whole functions on your own.

See: https://docs.aws.amazon.com/sdk-for-go/api/aws/

Jason Lee
  • 11
  • 1
-3

What the constants section does not make very clear: Constants are, unlike variables, not present in the compiled code or running program. They are untyped and will only be in memory once they are assigned to a variable.

As a result, they seem1 to have infinite precision. If you look at this example, you can see that I can assign the constant to a variable without casting it, and the variable will hold as much of the constants precision as it can.


1 As the spec also points out, integers have at least 256 bits, floats at least 256 bits mantissa and 32 bits exponent, and the compiler will throw an error if its internal constructs cannot accurately store a constant.

cfstras
  • 1,613
  • 15
  • 21