Goでリテラル* int64を実行するにはどうすればよいですか?


103

*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。これは、コンストラクター内で使用したり、リテラル構造体の値を記述したり、他の関数の引数として使用したりできます。しかし、ヘルパー関数や別のタイプの使用は、私が探している解決策ではありません。


1
intへのポインターは残念ですが、intはポインターと同じ量のスペースを占めるため、スペースを節約することはできません。NULL値を追加するだけで、通常は価値よりも複雑になります。ほとんどの場合、0で問題ありません。追加の値が必要な場合は「IsValidSomeField」boolも機能し、そのboolに適切な名前を付けると、その追加の値が必要な理由がわかりやすくなるため、読みやすくなります。
voutasaurus

2
あなたは、パッケージを使用することができますポインタを、たとえば、:var _ *int64 = pointer.Int64(64)
Xorの

回答:


212

Go言語仕様(アドレス演算子)では、数値定数(型なし定数や型付き定数ではない)のアドレスを取得できません。

オペランドはアドレス指定可能である必要があります。つまり、変数、ポインターの間接指定、またはスライスのインデックス付け操作のいずれかです。または、アドレス可能な構造体オペランドのフィールドセレクタ。または、アドレス可能な配列の配列インデックス操作。アドレッシング要件の例外として、x[の式の中の&x]は(括弧で囲まれた)複合リテラルでもある場合があります。

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

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

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) // Prints 5

ここで発生するのは、[]int641つの要素(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,
}

1
これは、2 instanceつの異なるオブジェクトが2つの異なるを指すため、プレースホルダーに関する質問の最後の例と機能的に異なりますint64。しかし、OPの意図は適切に満たされているようです
Ryan Haining

2
これは間違いなく私の質問に答えます。しかし、それは私をかなり怒らせます:&[]int64{2}[0]blog.golang.org /constantsの定数の説明に基づくと、これちょうど同じように動作するはず&0です。
ThisGuy

@RyanHainingそして、同じアドレスが割り当てられるように機能する場合はどうなりますか?2つの異なるinstanceオブジェクトが同じオブジェクトをポイントし、int641つがポイントしたオブジェクトを変更すると、両方が変化します。そして、今あなたが同じリテラルを使って3番目のものを作成するとしinstanceたらどうでしょう?同じアドレスは現在さまざまに指すことになりint64、別のアドレスを使用する必要がありますので...値が、その後、なぜ最初の2のケースでも同じでしょうか?
icza

@icza通常は、同じオブジェクトを指すようにしたくないのですが、そうは言っていません。私は違いを指摘しているだけです。
Ryan Haining

4
@Conslo定数はコンパイル時に評価されます。有効なポインタ値、有効なメモリアドレスは実行時にのみ存在するため、定数とは異なります。
icza

6

問題を解決するには、int64変数のアドレスを返す関数を使用します。

以下のコードでは、関数を使用f整数を受け入れ、整数のアドレスを保持するポインタ値を返します。この方法を使用すると、上記の問題を簡単に解決できます。

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.