How do I do a literal *int64 in Go?

2019-01-02 21:04发布

问题:

I have a struct type with a *int64 field.

type SomeType struct {
    SomeField *int64
}

At some point in my code, I want to declare a literal of this (say, when I know said value should be 0, or pointing to a 0, you know what I mean)

instance := SomeType{
    SomeField: &0,
}

...except this doesn't work

./main.go:xx: cannot use &0 (type *int) as type *int64 in field value

So I try this

instance := SomeType{
    SomeField: &int64(0),
}

...but this also doesn't work

./main.go:xx: cannot take the address of int64(0)

How do I do this? The only solution I can come up with is using a placeholder variable

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Note: the &0 syntax works fine when it's a *int instead of an *int64. Edit: no it does not. Sorry about this.

Edit:

Aparently there was too much ambiguity to my question. I'm looking for a way to literally state a *int64. This could be used inside a constructor, or to state literal struct values, or even as arguments to other functions. But helper functions or using a different type are not solutions I'm looking for.

回答1:

The Go Language Specification (Address operators) does not allow to take the address of a numeric constant (not of an untyped nor of a typed constant).

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 [in the expression of &x] may also be a (possibly parenthesized) composite literal.

For reasoning why this isn't allowed, see related question: Find address of constant in go. A similar question (similarly not allowed to take its address): How can I store reference to the result of an operation in Go?

Your options (try all on the Go Playground):

1) With new()

You can simply use the builtin new() function to allocate a new zero-valued int64 and get its address:

instance := SomeType{
    SomeField: new(int64),
}

But note that this can only be used to allocate and obtain a pointer to the zero value of any type.

2) With helper variable

Simplest and recommended for non-zero elements is to use a helper variable whose address can be taken:

helper := int64(2)
instance2 := SomeType{
    SomeField: &helper,
}

3) With helper function

Or if you need this many times, you can create a helper function which allocates and returns an *int64:

func create(x int64) *int64 {
    return &x
}

And using it:

instance3 := SomeType{
    SomeField: create(3),
}

4) With a one-liner anonymous function

instance4 := SomeType{
    SomeField: func() *int64 { i := int64(4); return &i }(),
}

Or as a (shorter) alternative:

instance4 := SomeType{
    SomeField: func(i int64) *int64 { return &i }(4),
}

5) With slice literal, indexing and taking address

If you would want *SomeField to be other than 0, then you need something addressable.

You can still do that, but that's ugly:

instance5 := SomeType{
    SomeField: &[]int64{5}[0],
}
fmt.Println(*instance2.SomeField) // Prints 5

What happens here is an []int64 slice is created with a literal, having one element (5). And it is indexed (0th element) and the address of the 0th element is taken. In the background an array of [1]int64 will also be allocated and used as the backing array for the slice. So there is a lot of boilerplate here.

6) With a helper struct literal

Let's examine the exception to the addressability requirements:

As an exception to the addressability requirement, x [in the expression of &x] may also be a (possibly parenthesized) composite literal.

This means that taking the address of a composite literal, e.g. a struct literal is ok. If we do so, we will have the struct value allocated and a pointer obtained to it. But if so, another requirement will become available to us: "field selector of an addressable struct operand". So if the struct literal contains a field of type int64, we can also take the address of that field!

Let's see this option in action. We will use this wrapper struct type:

type intwrapper struct {
    x int64
}

And now we can do:

instance6 := SomeType{
    SomeField: &(&intwrapper{6}).x,
}

Note that this

&(&intwrapper{6}).x

means the following:

& ( (&intwrapper{6}).x )

But we can omit the "outer" parenthesis as the address operator & is applied to the result of the selector expression.

Also note that in the background the following will happen (this is also a valid syntax):

&(*(&intwrapper{6})).x

7) With helper anonymous struct literal

The principle is the same as with case #6, but we can also use an anonymous struct literal, so no helper/wrapper struct type definition needed:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}


回答2:

Use a function which return an address of a int64 variable will solve the problem.

In the below code we use a function f which accepts integer and return a pointer value which holds the address of the integer. By using this method we can easily solve the above problem.

 type myStr struct {
        url *int64
    }

    func main() {

        f := func(s int64) *int64 {
            return &s
        }
        myStr{
            url: f(12345),
        }
    }


标签: