英文:
How do I do a literal *int64 in Go?
问题
我有一个结构体类型,其中包含一个 *int64
字段。
type SomeType struct {
SomeField *int64
}
在我的代码中的某个地方,我想要声明一个该类型的字面量(比如,当我知道该值应该为0,或者指向0时,你知道我的意思)。
instance := SomeType{
SomeField: &0,
}
...但是这样是行不通的。
./main.go:xx: cannot use &0 (type *int) as type *int64 in field value
所以我尝试了这样的方式。
instance := SomeType{
SomeField: &int64(0),
}
...但是这样也不起作用。
./main.go:xx: cannot take the address of int64(0)
我该如何做呢?我能想到的唯一解决办法是使用一个占位变量。
var placeholder int64
placeholder = 0
instance := SomeType{
SomeField: &placeholder,
}
注意:&0
的语法在 *int 类型而不是 *int64
类型时是可以工作的。编辑:不,它不行。对此我感到抱歉。
编辑:
显然我的问题存在太多的歧义。我正在寻找一种明确声明 *int64
的方法。这可以用于构造函数内部,或者用于声明字面量结构体值,甚至作为其他函数的参数。但是,辅助函数或使用不同的类型并不是我要寻找的解决方案。
英文:
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,
}
<s>Note: the &0
syntax works fine when it's a *int instead of an *int64
.</s> 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
得分: 349
Go语言规范(地址运算符)不允许获取数值常量的地址(无论是_untyped_还是_typed_常量)。
操作数必须是_addressable_,即变量、指针间接引用或切片索引操作;或者是可寻址结构操作数的字段选择器;或者是可寻址数组的数组索引操作。作为对可寻址要求的例外,
x
(在&x
的表达式中)也可以是(可能带括号的)复合字面量。
关于为什么不允许这样做的原因,请参考相关问题:https://stackoverflow.com/questions/35146286/find-address-of-constant-in-go/35146856#35146856。类似的问题(同样不允许获取其地址):https://stackoverflow.com/questions/34197248/how-can-i-store-reference-to-the-result-of-an-operation-in-go/34197367#34197367
0) 通用解决方案(从Go 1.18开始)
在Go 1.18中引入了泛型。这意味着我们可以创建一个通用的Ptr()
函数,它返回我们传递给它的任何值的指针。希望它会被添加到标准库中。在那之前,你可以使用github.com/icza/gog
库中的gog.Ptr()
函数(声明:我是作者)。
它的实现如下:
func Ptr[T any](v T) *T {
return &v
}
测试一下:
i := Ptr(2)
log.Printf("%T %v", i, *i)
s := Ptr("abc")
log.Printf("%T %v", s, *s)
x := Ptr[any](nil)
log.Printf("%T %v", x, *x)
输出结果(在Go Playground上尝试):
2009/11/10 23:00:00 *int 2
2009/11/10 23:00:00 *string abc
2009/11/10 23:00:00 *interface {} <nil>
在Go 1.18之前的其他选项(在Go Playground上尝试):
1) 使用new()
你可以简单地使用内置的new()
函数来分配一个新的零值int64
并获取其地址:
instance := SomeType{
SomeField: new(int64),
}
但请注意,这只能用于分配和获取任何类型的零值的指针。
2) 使用辅助变量
对于非零元素,最简单且推荐的方法是使用一个辅助变量,可以获取其地址:
helper := int64(2)
instance2 := SomeType{
SomeField: &helper,
}
3) 使用辅助函数
**注意:**获取非零值的指针的辅助函数在我的github.com/icza/gox
库的gox
包中可用,因此你不必在所有需要它的项目中都添加这些函数。
如果你需要多次使用这个功能,可以创建一个辅助函数,它分配并返回一个*int64
:
func create(x int64) *int64 {
return &x
}
使用它:
instance3 := SomeType{
SomeField: create(3),
}
请注意,实际上我们没有分配任何东西,当我们返回函数参数的地址时,Go编译器会自动进行分配。Go编译器执行逃逸分析,并在函数中返回的局部变量可能逃逸函数时,将局部变量分配在堆上(而不是栈上)。有关详细信息,请参见https://stackoverflow.com/questions/42530219/is-returning-a-slice-of-a-local-array-in-a-go-function-safe/42530418#42530418
4) 使用一行匿名函数
instance4 := SomeType{
SomeField: func() *int64 { i := int64(4); return &i }(),
}
或者作为(更短的)替代方法:
instance4 := SomeType{
SomeField: func(i int64) *int64 { return &i }(4),
}
5) 使用切片字面量、索引和取地址
如果你希望*SomeField
不为0
,那么你需要一个可寻址的值。
你仍然可以做到这一点,但是这样做很丑陋:
instance5 := SomeType{
SomeField: &[]int64{5}[0],
}
fmt.Println(*instance2.SomeField) // 输出 5
这里发生的情况是创建了一个带有字面值的[]int64
切片,其中有一个元素(5
)。然后对它进行索引(第0个元素),并获取第0个元素的地址。在后台,还将分配一个[1]int64
数组,并用作切片的支持数组。因此,这里有很多样板代码。
6) 使用辅助结构体字面量
让我们来看一下对可寻址要求的例外情况:
作为对可寻址要求的例外,
x
(在&x
的表达式中)也可以是(可能带括号的)复合字面量。
这意味着可以获取复合字面量(例如结构体字面量)的地址是可以的。如果我们这样做,我们将分配结构体值并获取其指针。但是,如果这样做,我们将获得另一个要求:“可寻址结构操作数的字段选择器”。因此,如果结构体字面量包含类型为int64
的字段,我们还可以获取该字段的地址!
让我们看看这个选项的实现。我们将使用以下包装结构体类型:
type intwrapper struct {
x int64
}
现在我们可以这样做:
instance6 := SomeType{
SomeField: &(&intwrapper{6}).x,
}
请注意,这里的
&(&intwrapper{6}).x
表示以下内容:
& ( (&intwrapper{6}).x )
但是我们可以省略“外部”括号,因为地址运算符&
应用于选择器表达式的结果。
还要注意,在后台将发生以下操作(这也是有效的语法):
&(*(&intwrapper{6})).x
7) 使用辅助匿名结构体字面量
原理与第6种情况相同,但我们还可以使用匿名结构体字面量,因此不需要辅助/包装结构体类型的定义:
instance7 := SomeType{
SomeField: &(&struct{ x int64 }{7}).x,
}
英文:
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: https://stackoverflow.com/questions/35146286/find-address-of-constant-in-go/35146856#35146856. A similar question (similarly not allowed to take its address): https://stackoverflow.com/questions/34197248/how-can-i-store-reference-to-the-result-of-an-operation-in-go/34197367#34197367
0) Generic solution (from Go 1.18)
Generics are added in Go 1.18. This means we can create a single, generic Ptr()
function that returns a pointer to whatever value we pass to it. Hopefully it'll get added to the standard library. Until then, you can use github.com/icza/gog
, the gog.Ptr()
function (disclosure: I'm the author).
This is how it can look like:
func Ptr[T any](v T) *T {
return &v
}
Testing it:
i := Ptr(2)
log.Printf("%T %v", i, *i)
s := Ptr("abc")
log.Printf("%T %v", s, *s)
x := Ptr[any](nil)
log.Printf("%T %v", x, *x)
Which will output (try it on the Go Playground):
2009/11/10 23:00:00 *int 2
2009/11/10 23:00:00 *string abc
2009/11/10 23:00:00 *interface {} <nil>
Your other options (prior to Go 1.18) (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
Note: Helper functions to acquire a pointer to a non-zero value are available in my github.com/icza/gox
library, in the gox
package, so you don't have to add these to all your projects where you need it.
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),
}
Note that we actually didn't allocate anything, the Go compiler did that when we returned the address of the function argument. The Go compiler performs escape analysis, and allocates local variables on the heap (instead of the stack) if they may escape the function. For details, see https://stackoverflow.com/questions/42530219/is-returning-a-slice-of-a-local-array-in-a-go-function-safe/42530418#42530418
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
得分: 7
如果您不介意使用第三方库,可以使用lo包,该包使用泛型(go 1.18+)并提供了.ToPtr()
函数。
ptr := lo.ToPtr("hello world")
// *string{"hello world"}
英文:
If you don't mind using third party libraries, there's the lo package which uses generics (go 1.18+) which has the .ToPtr()
function
ptr := lo.ToPtr("hello world")
// *string{"hello world"}
答案3
得分: 6
使用一个返回int64变量地址的函数来解决这个问题。
在下面的代码中,我们使用了一个名为f
的函数,它接受一个整数并返回一个指针值,该指针值保存了该整数的地址。通过使用这种方法,我们可以轻松解决上述问题。
type myStr struct {
url *int64
}
func main() {
f := func(s int64) *int64 {
return &s
}
myStr{
url: f(12345),
}
}
英文:
Use a function which return an address of an int64 variable to solve the problem.
> In the below code we use function f
which accepts an integer and
returns 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),
}
}
答案4
得分: 4
有另一种优雅的方法可以实现这一点,它不会产生太多样板代码,并且在我看来不会显得丑陋。如果我需要一个具有指向基本类型的指针而不是值的结构体,以确保项目中不使用零值结构体成员,我将创建一个带有这些基本类型参数的函数。
您可以定义一个函数来创建您的结构体,然后将基本类型参数传递给该函数,并使用函数参数的指针。
type Config struct {
Code *uint8
Name *string
}
func NewConfig(code uint8, name string) *Config {
return &Config{
Code: &code,
Name: &name,
}
}
func UseConfig() {
config := NewConfig(1, "test")
// ...
}
// 如果有很多值,现代 IDE 将为您突出显示参数名称,因此您不必记住
func UseConfig2() {
config := NewConfig(
1,
"test",
)
// ...
}
英文:
There is another elegant way to achieve this which doesn't produce much boilerplate code and doesn't look ugly in my opinion. In case I need a struct with pointers to primitives instead of values, to make sure that zero-valued struct members aren't used across the project, I will create a function with those primitives as arguments.
You can define a function which creates your struct and then pass primitives to this function and then use pointers to function arguments.
type Config struct {
Code *uint8
Name *string
}
func NewConfig(code uint8, name string) *Config {
return &Config{
Code: &code,
Name: &name,
}
}
func UseConfig() {
config := NewConfig(1, "test")
// ...
}
// in case there are many values, modern IDE will highlight argument names for you, so you don't have to remember
func UseConfig2() {
config := NewConfig(
1,
"test",
)
// ...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论