1. ホーム
  2. pointers

[解決済み] Goで*int64をリテラルにするにはどうしたらいいですか?

2022-05-31 22:20:16

質問

構造体型に *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 . 編集:いいえそうではありません。これは申し訳ありません。

編集

どうやら私の質問には曖昧な点が多すぎたようです。私が探しているのは 文字通りの状態 a *int64 . これはコンストラクタの内部で使用したり、リテラルな構造体の値を指定したり、他の関数の引数として使用したりすることができます。しかし、ヘルパー関数や別の型を使用することは、私が求めている解決策ではありません。

どのように解決するのですか?

Go言語仕様( アドレス演算子 ) では、数値定数のアドレスを取ることはできません。 型でない でもなく タイプされた 定数)でもありません。

オペランドは必ず アドレス指定可能 すなわち、変数、ポインタインディレクション、スライスインデックス操作、アドレス指定可能な構造体オペランドのフィールドセレクタ、またはアドレス指定可能な配列のインデックス操作のいずれかでなければなりません。アドレス指定可能の要件の例外として x [の式中]。 &x は、(おそらく括弧でくくられた) 複合リテラル .

これが許されない理由については、関連する質問を参照してください。 goの定数のアドレスを見つける . 同様の質問(同様にそのアドレスを取ることは許可されていません)。 どのように私はGoで操作の結果への参照を格納することができますか?

あなたの選択肢 (すべての 囲碁の遊び場 ):

1)一緒に new()

を使用すると、単純に組み込みの new() 関数を使用して、新しいゼロ値 int64 を割り当て、そのアドレスを取得します。

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

しかし、これは任意の型のゼロ値へのポインタを割り当て、取得するためにのみ使用できることに注意してください。

2)ヘルパー変数を使う

最も簡単で、0以外の要素のために推奨されるのは、アドレスを取得できるヘルパー変数を使用することです。

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 コンパイラーはエスケープ解析を行い、関数をエスケープする可能性がある場合は(スタックではなく)ヒープにローカル変数を割り当てます。詳しくは Go 関数でローカル配列のスライスを返すのは安全ですか?

Go 1.18 ジェネリックの更新。 Go 1.18でジェネリックが追加されました。これは、単一の汎用的な create() 関数を作成できることを意味します。うまくいけば、標準ライブラリに追加されるかもしれません。

こんな感じでいいんじゃないでしょうか。

func Ptr[T any](t T) *T {
    return &t
}

テスト中です。

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)

と出力されます(試しに 囲碁の遊び場 ):

2009/11/10 23:00:00 *int 2
2009/11/10 23:00:00 *string abc
2009/11/10 23:00:00 *interface {} <nil>

4) ワンライナーの無名関数で

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

あるいは、(より短い)代替案として

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

5) スライスリテラルで、インデックス付けとアドレスの取得

もし、あなたが *SomeField0 である場合、アドレス指定可能な何かが必要です。

まだできるけど、それは醜い。

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

ここで起こるのは []int64 のスライスがリテラルで作成され、1つの要素 ( 5 ). そしてそれはインデックスされ(0番目の要素)、0番目の要素のアドレスが取得されます。バックグラウンドでは [1]int64 の配列も確保され、スライスのバック配列として使用されます。というわけで、ここにはたくさんの定型文があります。

6) ヘルパー構造体リテラルで

アドレス指定可能な要件の例外を検証してみましょう。

アドレス指定可能な要件の例外として x [の表現では]。 &x は、(おそらく括弧でくくられた) 複合リテラル .

つまり、複合リテラル、例えば構造体リテラルのアドレスを取得してもOKということです。そうすれば、構造体の値が割り当てられ、それへのポインタが取得されることになります。しかし、そうすると、別の要件が利用できるようになる。 "アドレス指定可能な構造体オペランドのフィールドセレクタ"。 . したがって、もし構造体リテラルが型 int64 を含む場合、そのフィールドのアドレスを取得することもできます。

このオプションを実際に見てみましょう。このラッパー構造体型を使用します。

type intwrapper struct {
    x int64
}

そして、今できることは

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

なお、この

&(&intwrapper{6}).x

は以下を意味します。

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

しかし、アドレス演算子として "outer" を省略することができます。 & の結果に対して適用されるからです。 セレクタ式の結果に適用されます。 .

また、バックグラウンドでは以下のようになることに注意してください(これも有効な構文です)。

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

7) 匿名構造体リテラルの場合

原理はケース 6 と同じですが、匿名構造体リテラルを使用できるため、ヘルパー/ラッパー構造体の型定義は必要ありません。

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