package main
import "fmt"
type UserID struct {
Name string
Age int
}
type UserProfile struct {
Name string
Tags []string
}
func main() {
a := UserID{"Ann", 30}
b := UserID{"Ann", 30}
fmt.Println(a == b)
x := UserProfile{Name: "Ann", Tags: []string{"go"}}
y := UserProfile{Name: "Ann", Tags: []string{"go"}}
_, _ = x, y
// fmt.Println(x == y) // compile error: struct containing []string cannot be compared
}
type Status int
const (
StatusPending Status = iota // 0, typed Status
StatusActive // 1, same expression/type repeated
StatusClosed // 2
)
const big = 1 << 100 // untyped integer constant with high precision
var x int64 = 42 // untyped 42 can become int64
// var y int64 = big // compile error: not representable as int64
var x int64 = 300
var y int8 = int8(x) // explicit conversion; value changes
var f float64 = 3.9
var i int = int(f) // truncates toward zero
const c = 42
var n int32 = c // ok: untyped constant is representable as int32
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a, b)
s := []int{1, 2, 3}
t := s
t[0] = 99
fmt.Println(s, t)
fmt.Println(len(s), cap(s))
}
Goのmapのキーの型は比較可能(comparable)である必要があり、スライス、マップ、関数を直接キーとして使用することはできません。存在しないキーを参照すると要素型のゼロ値が返されるため、存在しないことと格納されているゼロ値を区別するにはcomma-ok構文(`v, ok := m[k]`)を使用します。nilマップからの読み出しやrangeによる反復処理は可能ですが、代入を行うとパニックが発生するため、書き込み前に初期化する必要があります。マップの反復順序は未規定(unspecified)であり、コードがその順序に依存してはなりません。
package main
import "fmt"
func main() {
counts := map[string]int{"a": 0, "b": 2}
fmt.Println(counts["missing"]) // zero value for int
v, ok := counts["a"]
fmt.Println(v, ok) // present even though value is zero
var m map[string]int
fmt.Println(m["x"]) // read from nil map is OK
// m["x"] = 1 // panic: assignment to entry in nil map
m = make(map[string]int)
m["x"] = 1
for k, v := range counts {
fmt.Println(k, v) // order is not guaranteed
}
}
package main
import "fmt"
type Point struct{ X int }
func main() {
a := Point{X: 1}
b := a
b.X = 2
x := [2]int{1, 2}
y := x
y[0] = 99
fmt.Println(a.X, b.X)
fmt.Println(x, y)
}
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(v int) {
defer wg.Done()
fmt.Println(v)
}(i)
}
wg.Wait()
}
12配列、スライス、マップ、文字列、チャネルに対する range のセマンティクスについて説明してください。
`range` は対象のオペランドの型に応じて反復処理を行います。配列またはスライスに対してはインデックスと要素の値を返します。要素の値はコピーであるため、代入しても元のコレクションは変更されません。配列の値に対する range は反復処理のために配列全体をコピーしますが、スライスに対する range はスライスヘッダーを使用し、背後にある基底配列をインデックス参照します。マップに対してはキーと値を順不同で返し、値はコピーとなります。文字列に対してはバイトインデックスとデコードされたUnicodeコードポイント(`rune`)を返します(文字位置のインデックスではありません)。チャネルに対しては、チャネルがクローズされ空になるまで値を受信し続けます。なお、nil チャネルに対する range は永久にブロックします。
package main
import "fmt"
func main() {
xs := []int{1, 2, 3}
for _, v := range xs {
v *= 10 // modifies only the copy
}
fmt.Println(xs)
for i := range xs {
xs[i] *= 10 // modifies the slice element
}
fmt.Println(xs)
}
値レシーバを持つメソッドはレシーバのコピーを受け取るため、読み取り専用の振る舞いや、イミュータブルな設計の小さな型に適しています。レシーバのコピーに対する変更は呼び出し元には反映されません。一方、ポインタレシーバを持つメソッドはレシーバへのポインタのコピーを受け取るため、元の値を変更でき、サイズが大きい値やコピーすべきでない値のコピーを回避できます。レシーバの選択は method set(メソッドセット)に影響します。値レシーバのメソッドは T と *T の両方のメソッドセットに含まれますが、ポインタレシーバのメソッドは *T のメソッドセットにのみ含まれます。そのため、ポインタレシーバのメソッドを要求するインターフェースは *T によって充足され、T では充足されません。これは、アドレス取得可能な T の値から通常のメソッド呼び出し構文を使ってそのメソッドを呼び出せる場合であっても同様です。
package main
import "fmt"
type Counter struct{ N int }
func (c Counter) AddValue() { c.N++ }
func (c *Counter) AddPointer() { c.N++ }
func main() {
c := Counter{N: 1}
c.AddValue()
fmt.Println(c.N)
c.AddPointer() // compiler rewrites to (&c).AddPointer() because c is addressable
fmt.Println(c.N)
}
型アサーションはインターフェイスに格納されている動的な値を検査します。`v := x.(T)`は、インターフェイス値の動的型が`T`であるか、または`T`が動的な値によって実装されているインターフェイスである場合に成功します。戻り値が1つの形式では、失敗時にパニックが発生します。カンマ・ok形式の`v, ok := x.(T)`は、パニックを起こさずに成功の成否を返します。型スイッチである`switch v := x.(type)`は、インターフェイス値の動的型に基づいて分岐し、`v`は一致したcaseに応じた型として扱われます。
func describe(x any) string {
if s, ok := x.(string); ok {
return "string: " + s
}
switch v := x.(type) {
case int:
return fmt.Sprintf("int: %d", v)
case fmt.Stringer:
return "stringer: " + v.String()
case nil:
return "nil interface"
default:
return fmt.Sprintf("unknown %T", v)
}
}