Fork me on GitHub

spec

Goの仕様に関する、あるいは関するらしい事柄ではまったことをまとめていきたい

値型のみで構成される構造体は直接アドレスを取得できない

以下のコードはコンパイルエラーになります。

package main

type OnlyString string

func main() {
    _ = OnlyString("hoge")

    // 以下はコンパイラエラーになる
    _ = &OnlyString("hoge")
}

https://play.golang.org/p/A1pfwbljldj

直感的には string が値型なので値型のアドレスを取得することができないため、という理解です。がGoの以下の仕様を確認しました。

https://golang.org/ref/spec#Address_operators

For an operand x of type T, the address operation &x generates a pointer of type *T to x. 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 may also be a (possibly parenthesized) composite literal. If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.

つまり & を用いてアドレスを取得できるのは以下の場合のみです。

  • 変数

  • ポインタ参照

  • スライスのindex操作

  • アドレス化可能な構造体のフィールド

  • アドレス化可能な配列のindex操作

以下はいずれも有効なコードです。

変数

package main

import "fmt"

type OnlyString string

func main() {
    h := OnlyString("hoge")
    fmt.Println(&h)
}

ポインタ参照

package main

import "fmt"

func main() {
    s := "hoge"
    sp := &s
    fmt.Println(&sp)
}

スライスのindex操作

package main

import "fmt"

func main() {
    s := []int{1, 2, 3}
    fmt.Println(&s[0])
}

アドレス化可能な構造体のフィールド

package main

import "fmt"

type A struct {
    s string
}

type B struct {
    a A
}

func main() {
    b := &B{a: A{s: "foo"}}
    fmt.Println(b)
}

アドレス化可能な配列のindex操作

package main

import "fmt"

func main() {
    s := [3]int{1, 2, 3}
    fmt.Println(&s[0])
}

エラーが無限ループする

題材は A Tour of Go の Exercise からです。

https://go-tour-jp.appspot.com/methods/20

以下は完全に正しいコードです。

package main

import (
    "fmt"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

func Sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0, ErrNegativeSqrt(x)
    }
    z := 1.0
    for i := 0; i < 10; i++ {
        z -= (z*z - x) / (2 * z)
    }
    return z, nil
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

上記のコードに1行変更を加えてみます。以下のコードは無限ループします。

package main

import (
    "fmt"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
-    return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
+    return fmt.Sprintf("cannot Sqrt negative number: %v", e)
}

func Sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0, ErrNegativeSqrt(x)
    }
    z := 1.0
    for i := 0; i < 10; i++ {
        z -= (z*z - x) / (2 * z)
    }
    return z, nil
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

https://play.golang.org/p/tikMDE_Sq_R

1.414213562373095 <nil>
runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x117172, 0xe)
    /usr/local/go/src/runtime/panic.go:774 +0x80
runtime.newstack()
    /usr/local/go/src/runtime/stack.go:1046 +0x960
runtime.morestack()
    /usr/local/go/src/runtime/asm_amd64p32.s:300 +0xc0
...

Error()string の呼び出しに等しいので、以下と言いかえられます。これも無限ループします。

type MyType struct {
    f float64
}

func (m MyType) String() string {
    return fmt.Sprintf("MyType: %v", m)
}

func main() {
    m := MyType{-1}
    fmt.Println(m)
}