20200926のGoに関する記事は8件です。

【Golang】Go言語の基礎 値レシーバーとポインタレシーバーについて

今回はGo言語の値レシーバーとポインタレシーバーについてやっていきます!
この記事の後に、こちらの記事まで読んでいただくとよりGo言語に関する理解が深まると思います!

値レシーバーとは

package main

import "fmt"

type Vertex struct {
    X, Y int
}

func (v Vertex) Area() int {
    return v.X * v.Y
}

//値レシーバー
func (v Vertex) Scale(i int) {
    v.X = v.X * i
    v.Y = v.Y * i
}

func main() {
    v := Vertex{3, 4}
    v.Scale(10)
    fmt.Println(v.Area()) // => 12
}

こちらは値レシーバになります。
めっちゃ乱暴にいうと*がなければ値レシーバーになります。値渡しとか言われるようです。

v.Scale(10)で引数を渡しており、Scale関数の中では v.X = 3 * 10 v.Y = 4 * 10 となります。
しかし、値渡しでは直接Vertexの中身を書き換えることができません。あくまでもScale関数のスコープ内で値が書き換わっただけです。
よってArea関数では初期値のX,Yが使われるので返り値として 3 * 4 = 12が出力されます。

【補足】
func (v Vertex) Area()と書くことによってVertexとArea()関数を紐づけることができます。
こちらのメソッドを呼び出すときは、v.Area()と書くと呼び出すことができます。
Goにはクラスという概念がありませんが、このようにすることによってオブジェクト指向のような書き方ができます。
(rubyでいうとUser.hogeみたいな感じですね)

値レシーバー

package main

import "fmt"

type Vertex struct {
    X, Y int
}

func (v Vertex) Area() int {
    return v.X * v.Y
}

//ポインタレシーバー(*がついただけ)
func (v *Vertex) Scale(i int) {
    v.X = v.X * i
    v.Y = v.Y * i
}

func main() {
    v := Vertex{3, 4}
    v.Scale(10)
    fmt.Println(v.Area()) // => 1200
}

変数名のとなりに*をつけただけです!
これをすることによって構造体の中身を書き換えることができます。

よってScale関数の引数で渡されたときに v.X = 30, v.Y = 40 となりそれがそのままArea関数に渡ります。

よってv.Area()の出力結果は1200となります!

最後まで読んでいただきありがとうございます!

なにか間違いや、ご質問などありましたら気軽にコメントもらえると嬉しいです!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ACL Beginner Contest のメモ

前置き

Atcoderをやってみたので、自分用のメモです。
あとから加筆・修正する予定です。

問題

https://atcoder.jp/contests/abl

A

Q_A.go
package main

import (
    "fmt"
)

func main() {
    var k int
    fmt.Scanf("%d", &k)

    var s string = ""
    for i := 0; i<k; i++{
        s += "ACL"
    }

    fmt.Printf("%s\n", s)
}

B

Q_B.go
package main

import (
    "fmt"
)

func main() {
    var a, b, c, d uint64
    fmt.Scanf("%d %d %d %d", &a, &b, &c, &d)

    var s string
    if (a<=d && c <= b) || (c <= b && a<=d) {
        s = "Yes"
    } else {
        s = "No"
    }

    fmt.Printf("%s\n", s)
}

C

覚えてたら後で書きます。

D

覚えてたら後で書きます。

E

覚えてたら後で書きます。

F

覚えてたら後で書きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ACL Beginner Contest 参戦記

ACL Beginner Contest 参戦記

ABLA - Repeat ACL

1分で突破. 書くだけ.

K = int(input())

print('ACL' * K)

ABLB - Integer Preference

2分半で突破、WA1 orz. A < B < C < D のパターンを見落とした.

A, B, C, D = map(int, input().split())

if A <= C <= B or A <= D <= B or C <= A <= D or C <= B <= D:
    print('Yes')
else:
    print('No')

ABLC - Connect Cities

3分半で突破. Union Find して Union 数 - 1 が答え.

from sys import setrecursionlimit, stdin


def find(parent, i):
    t = parent[i]
    if t < 0:
        return i
    t = find(parent, t)
    parent[i] = t
    return t


def unite(parent, i, j):
    i = find(parent, i)
    j = find(parent, j)
    if i == j:
        return
    parent[j] += parent[i]
    parent[i] = j


readline = stdin.readline
setrecursionlimit(10 ** 6)

N, M = map(int, readline().split())

parent = [-1] * N
for _ in range(M):
    A, B = map(lambda x: int(x) - 1, readline().split())
    unite(parent, A, B)

print(sum(1 for i in range(N) if parent[i] < 0) - 1)

ABLD - Flat Subsequence

32分で突破、WA1 orz. Go のコードを Python で提出した、アホすぎる. それまでの各値の最大経由数を記録しておけば SegmentTree で O(logN) で現値の最大経由数が求まるので O(NlogN) になり解ける.

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

func min(x, y int) int {
    if x < y {
        return x
    }
    return y
}

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}

type segmentTree struct {
    offset int
    data   []int
    op     func(x, y int) int
    e      int
}

func newSegmentTree(n int, op func(x, y int) int, e int) segmentTree {
    var result segmentTree
    t := 1
    for t < n {
        t *= 2
    }
    result.offset = t - 1
    result.data = make([]int, 2*t-1)
    for i := 0; i < len(result.data); i++ {
        result.data[i] = e
    }
    result.op = op
    result.e = e
    return result
}

func (st segmentTree) update(index, value int) {
    i := st.offset + index
    st.data[i] = value
    for i >= 1 {
        i = (i - 1) / 2
        st.data[i] = st.op(st.data[i*2+1], st.data[i*2+2])
    }
}

func (st segmentTree) query(start, stop int) int {
    result := st.e
    l := start + st.offset
    r := stop + st.offset
    for l < r {
        if l&1 == 0 {
            result = st.op(result, st.data[l])
        }
        if r&1 == 0 {
            result = st.op(result, st.data[r-1])
        }
        l = l / 2
        r = (r - 1) / 2
    }
    return result
}

const (
    maxA = 300000
)

func main() {
    defer flush()

    N := readInt()
    K := readInt()

    st := newSegmentTree(maxA+1, max, 0)
    for i := 0; i < N; i++ {
        A := readInt()
        st.update(A, st.query(max(A-K, 0), min(A+K+1, maxA+1))+1)
    }

    println(st.query(0, maxA+1))
}

const (
    ioBufferSize = 1 * 1024 * 1024 // 1 MB
)

var stdinScanner = func() *bufio.Scanner {
    result := bufio.NewScanner(os.Stdin)
    result.Buffer(make([]byte, ioBufferSize), ioBufferSize)
    result.Split(bufio.ScanWords)
    return result
}()

func readString() string {
    stdinScanner.Scan()
    return stdinScanner.Text()
}

func readInt() int {
    result, err := strconv.Atoi(readString())
    if err != nil {
        panic(err)
    }
    return result
}

var stdoutWriter = bufio.NewWriter(os.Stdout)

func flush() {
    stdoutWriter.Flush()
}

func println(args ...interface{}) (int, error) {
    return fmt.Fprintln(stdoutWriter, args...)
}

追記: Python では TLE になってしまうが、PyPy なら AC した.

class SegmentTree:
    def __init__(self, size, op, e):
        self._op = op
        self._e = e
        self._size = size
        t = 1
        while t < size:
            t *= 2
        self._offset = t - 1
        self._data = [e] * (t * 2 - 1)

    def update(self, index, value):
        op = self._op
        data = self._data
        i = self._offset + index
        data[i] = value
        while i >= 1:
            i = (i - 1) // 2
            data[i] = op(data[i * 2 + 1], data[i * 2 + 2])

    def query(self, start, stop):
        def iter_segments(data, l, r):
            while l < r:
                if l & 1 == 0:
                    yield data[l]
                if r & 1 == 0:
                    yield data[r - 1]
                l = l // 2
                r = (r - 1) // 2
        op = self._op
        it = iter_segments(self._data, start + self._offset,
                           stop + self._offset)
        result = self._e
        for v in it:
            result = op(result, v)
        return result


max_A = 300000

N, K, *A = map(int, open(0).read().split())

st = SegmentTree(max_A + 1, max, 0)
for a in A:
    st.update(a, st.query(max(a - K, 0), min(a + K + 1, max_A + 1)) + 1)

print(st.query(0, max_A + 1))

ABLE - Replace Digits

突破できず. Lazy Segtree なんだろうなとは思ったけど、ACL の lazy_segtree の mapping, composition, id が理解できなくて利用できなかった.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cgoのg++用インクルードパス

分かってしまったらなんてことはないが、調べてもなかなか出てこなくて詰まったのでメモ。

環境:go version go1.15.2 linux/amd64

CGOでインクルードパスを指定するときには
// #cgo CFLAGS: -I<インクルードパス>
とすればよいが、これはgccでのコンパイルのときにしか使用されない。
cppファイルはg++でコンパイルされるので、この時のインクルードパスを指定したければ
// #cgo CPPFLAGS: -I<インクルードパス>
とする。以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

golangでPre-Signed URL(署名付きURL)を生成する

GolangでPreSignedUrlを発行するコードを書いたので残しておきます。

package main

import (
    "context"
    "fmt"
    "time"
    "os"


    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/credentials"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
)

    // S3の接続情報
    accessKey := os.Getenv("AWS_ACCSESS_KEY")
    secretKey := os.Getenv("AWS_SECRET_KEY")
    region := aws.String(os.Getenv("CUEE_DB_REGION"))

    creds := credentials.NewStaticCredentials(accsessKey, secretKey, "")
    session := session.Must(session.NewSession(&aws.Config{
        Credentials: creds,
        Region:      region,
    }))
    svc := s3.New(session)
    c, _ := svc.PutObjectRequest(&s3.PutObjectInput{
        Bucket: aws.String("バケット名"),
        Key:    aws.String("/diectory/filename"),
    })

    url, err := c.Presign(15 * time.Minute)
    if err != nil {
        fmt.Println("error presigning request", err)
        return nil, err
    }

    fmt.Println(url)
}

ここで有効期限を設定できます

url, err := c.Presign(15 * time.Minute)

公式
https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/s3-example-presigned-urls.html

Bodyにファイルを含め、発行されたURLにアクセスすればアップロードできます

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Golangの環境構築

本記事はGo初心者の備忘録になります。

環境

OS: Mac Catalina
Shell: Zsh

Go のインストール

homebrew でGoをインストール

brew install go 

インストールが完了したか確認(バージョンも合わせて確認)

go version
# 出力結果例) go version go1.15.2 darwin/amd64

環境変数$GOROOTの確認(Goのインストール先)

go env GOROOT
# 出力結果例) /usr/local/Cellar/go/1.15.2/libexec

環境変数$GOPATHの確認

go env GOPATH
# 出力結果例) /Users/ユーザー名前/go

ls $(go env GOPATH)
# 出力結果例) ls: /Users/ユーザー名前/go: No such file or directory

※パッケージのインストールを終えていない段階ではまだ$GOPATHのディレクトリは存在しない

Goの環境設定

パッケージのインストール(パッケージはなんでもいい)

go get github.com/motemen/gore/cmd/gore

GOPATHの確認とパッケージの確認

ls $(go env GOPATH)
# 出力結果) bin/ src/

bin/ にはバイナリコード、src/ にはソースコードが格納されている。

GOPATHの設定をする(インストールしたパッケージをコマンドとして利用可能にする)

vim ~/.zshrc

下記の内容を ~/.zshrc の最後に追記

~/.zshrc
"Golangのパス設定
export GOPATH=$(go env GOPATH)
export PATH=$PATH:$GOPATH/bin

保存して閉じた後、~/.zshrc の再読み込み

source ~/.zshrc

インストールしたパッケージがコマンドラインで使えるようになったかの確認

gore は ruby の irb のように対話的にgoを実行できる

gore -autoimport
 gore>
 gore> fmt.Println(Hello World)
 Hello World
 12
 <nil>

↑12バイト出力されて、エラーがなかったという意味
ctrl + d で終了

goreがより便利になる拡張ツールも合わせてインストール

コード補完、出力のハイライト、APIドキュメントの参照などができるようにする。

go get github.com/mdempsky/gocode
go get github.com/k0kubun/pp

プロジェクト管理ツールの設定

プロジェクト管理ツール ghq のインストール

brew install ghq
ghq の確認(自端末にインストールされているGoパッケージ(プロジェクト)の一覧を表示する)
ghq list

# 出力結果例)
# golang.org/x/tools
# golang.org/x/xerrors
# golang.org/x/sys
# golang.org/x/text
# golang.org/x/mod
# github.com/peterh/liner
# github.com/k0kubun/pp
# github.com/motemen/gore
# github.com/motemen/go-quickfix
# github.com/mdempsky/gocode
# github.com/mattn/go-isatty
# github.com/mattn/go-runewidth
# github.com/mattn/go-colorable

※自分で何かプロジェクトの作成を行う際も上記と同じ階層で開発を行う。

ghqでソースを取得してくるディレクトリを指定
git config --global ghq.root $GOPATH/src

ghq は裏で git コマンドを叩いていると思われる。

他の細かいところは必要になったら対応する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Golang]`errors.Is()` `errors.As()` 完全ガイド〜使い方と違いをしっかり調査しました〜

はじめに

errors.As()を雰囲気で使っていたらハマったので、errors.Is()も含めて、しっかりと調査してドキュメントとコードを読んだ上でまとめてみました。

ハマったところ、ハマりそうなところを重点的にまとめてみたので、お役に立てれば幸いです。

何をするメソッドなのか

簡単に

errorを比較してboolを返してくれます。
使いみちとしては、アプリケーションのエラーを外部のエラー(例:gRPCのエラー)に変換したり、ライブラリで使用されているエラーをハンドリングして、アプリケーションのエラーに変換したりするときがあると思います。

errors.Is()

  • 比較対象が保持している値と比較します。
    • 値が同じならtrueを返します。
    • 値が異なるならfalseを返します。
  • interfaceなど比較できない者同士だと必ずfalseになります。

errors.As()

  • 比較対象を型レベルで比較します。
    • 型が同じならtrueを返します。
    • 型が異なるならfalseを返します。
  • 値は違ってもtrueを返します。
  • 引数target(第2引数)には、nilではないポインタ型を渡しましょう。

詳しく

errors.Is()のGoDocとコードを読みました

Is reports whether any error in err's chain matches target.
The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true.
An error type might provide an Is method so it can be treated as equivalent to an existing error.
https://golang.org/pkg/errors/#Is

そのまま訳すと...

Is() は、errのチェーン内のエラーがターゲットにマッチするかどうかを報告します。
このチェーンはerr自身の後に、Unwrapを繰り返し呼び出すことで得られる一連のエラーで構成されています。
エラーがターゲットと等しい場合、または Is(target)trueを返すような Is(error) bool メソッドを実装している場合、エラーはターゲットと一致しているとみなされます。
エラーの型は、既存のエラーと同等の扱いができるように、Is()メソッドを提供している場合があります。

要するに、errors.Is()は、エラーを比較して同じ値を持っていたらtrue、持っていないならfalseを返してくれます。

注目する点は...

エラーがターゲットと等しい場合

は、Is()の実装は必要無いということです。

もっというと、

エラーの値が比較可能であれば、Is()の実装は必要無いです。
エラーの値が比較不可能であれば、Is()の実装は必要です。

あとは、Wrapしたエラーには使える無いも抑えておくべきです!
→ Wrapしたエラーと比較するときは、errors.As()を使いましょう。

エラーの値が比較可能なとき

例えば、structError()を実装しerror interfaceを満たして、errorとして扱っている場合のことです。
struct同士は比較可能なので、Is()の実装は必要ありません。

実際のコードを見ると、値を比較できるまでUnwrap()して、比較可能になった時点で、比較していることがわかります。

    isComparable := reflectlite.TypeOf(target).Comparable()
    for {
        if isComparable && err == target {
            return true
        }
        if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
            return true
        }
        // TODO: consider supporting target.Is(err). This would allow
        // user-definable predicates, but also may allow for coping with sloppy
        // APIs, thereby making it easier to get away with them.
        if err = Unwrap(err); err == nil {
            return false
        }
    }

https://github.com/golang/go/blob/master/src/errors/wrap.go#L44-L58

エラーの値が比較 不 可能なとき

例えば、error interfaceを内包したstruct同士を比較したときなどです。
interface同士の比較はできません。

isComparable := reflectlite.TypeOf(target).Comparable()

https://github.com/golang/go/blob/master/src/errors/wrap.go#L44

ここにfalseが入るわけです。

下記のサンプルコードでは、interface errorは比較できないので、falseになっています。

type originalError struct{ err error }

func (e originalError) Error() string { return e.err.Error() }

func main() {
    is := errors.Is(originalError{err: errors.New("1")}, originalError{err: errors.New("1")})
    fmt.Printf("is = %v because err is not comparable\n", is)
}

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

なので、Is()を実装して、比較する必要があります。
実装されたIs()では、err.Error()の結果stringを使って比較を行っているので、trueが返ります。

type originalError struct{ err error }

func (e originalError) Error() string { return e.err.Error() }

// implemented!!
func (e originalError) Is(target error) bool { return e.err.Error() == target.Error() }

func main() {
    is := errors.Is(originalError{err: errors.New("1")}, originalError{err: errors.New("1")})
    fmt.Printf("is = %v because originaleError implements Is()\n", is)
}

https://play.golang.org/p/5p8u-D1Hr6q

Wrapしたエラーには使えない

なぜなら、値を比較するのでWrapされた時点で値は比較対象とは異なるはずだからです。
先程も書きましたが、Wrapされたエラーと比較したいならerrors.As()を使いましょう!

ちなみに、エラーをラップするためには標準パッケージを使うと

fmt.Errorf("failed to do something: %w", err)

みたいな感じでWrapできます。

errors.As()のGoDocとコードを読みました

As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true. Otherwise, it returns false.
The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
An error matches target if the error's concrete value is assignable to the value pointed to by target, or if the error has a method As(interface{}) bool such that As(target) returns true. In the latter case, the As method is responsible for setting target.
An error type might provide an As method so it can be treated as if it were a different error type.
As panics if target is not a non-nil pointer to either a type that implements error, or to any interface type.
https://golang.org/pkg/errors/#As

そのまま訳すと...

As()errのチェインの中で最初のエラーが target にマッチするものを見つけ、マッチしていれば target をそのエラー値に設定してtrueを返します。そうでなければfalseを返します。
チェーンは err 自体の後に、Unwrapを繰り返し呼び出すことで得られる一連のエラーで構成されています。
エラーの具体的な値が target が指す値に代入可能な場合、またはエラーが As(target)trueを返すような As(interface{}) bool メソッドを持っている場合、エラーは target にマッチします。後者の場合は、As()メソッドがtargetの設定を担当します。
エラータイプが As()メソッドを提供している場合は、それが別のエラータイプであるかのように扱うことができます。
As()は、target がエラーを実装した型、または任意のinterfaceへのnilではないポインタである場合にパニックを起こします。

要するに、errors.As()は、エラーを比較して同じ型であればtrue、異なる型ならfalseを返してくれます。

注目する点は...

  • panicになる条件を抑えること。
  • 比較対象の値は同じでは無くて良くて、代入可能であればいいこと。
  • Wrapしたエラーにも使えること。

です。

panicになる条件① 比較対象がnilもしくは、pointerではない型である

    if target == nil {
        panic("errors: target cannot be nil")
    }
    val := reflectlite.ValueOf(target)
    typ := val.Type()
    if typ.Kind() != reflectlite.Ptr || val.IsNil() {
        panic("errors: target must be a non-nil pointer")
    }

https://github.com/golang/go/blob/master/src/errors/wrap.go#L78-L85

実際にpanicを起こしてみる

比較対象がnilのとき

type originalError struct{ err error }

func (e originalError) Error() string { return e.err.Error() }

func main() {
    err := &originalError{err: errors.New("1")}
    as := errors.As(err, nil)
    fmt.Printf("as = %v\n", as)
}

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

実行結果

./prog.go:14:8: second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type
Go vet exited.

panic: errors: target cannot be nil

goroutine 1 [running]:
errors.As(0x4deb00, 0xc000010210, 0x0, 0x0, 0xc000032778)
    /usr/local/go-faketime/src/errors/wrap.go:79 +0x5f5
main.main()
    /tmp/sandbox800961272/prog.go:14 +0x9f

比較対象がpointerではない型のとき

type originalError struct{ err error }

func (e originalError) Error() string { return e.err.Error() }

func main() {
    err := &originalError{err: errors.New("1")}
    var target originalError
    as := errors.As(err, target) // 本当は &target とするべき
    fmt.Printf("as = %v\n", as)
}

https://play.golang.org/p/iF-43pCJf3P

実行結果

./prog.go:15:8: second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type
Go vet exited.

panic: errors: target must be a non-nil pointer

goroutine 1 [running]:
errors.As(0x4deae0, 0xc00010a050, 0x4ae1c0, 0xc00010a060, 0xc000068f48)
    /usr/local/go-faketime/src/errors/wrap.go:84 +0x54f
main.main()
    /tmp/sandbox214256996/prog.go:15 +0xd1

panicになる条件② 比較対象がerror interfaceを実装していない

そもそもコンパイルできないので具体例を載せることは割愛しますが、要注意です!

    if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) {
        panic("errors: *target must be interface or implement error")
    }

https://github.com/golang/go/blob/master/src/errors/wrap.go#L86-L88

panicになる条件」と「比較対象の値は同じでは無くて良くて、代入可能であればいいこと」を踏まえて実装してみる

2つのパターンを用意してみました。

enumを使った実装

type ErrorCode uint64

const (
    Zero ErrorCode = iota
    One
)

func (code ErrorCode) Error() string {
    return [...]string{
        "Error: Zero",
        "Error: One",
    }[code]
}
func main() {
    var code ErrorCode
    as := errors.As(Zero, &code)
    fmt.Printf("as = %v\n", as)
}

https://play.golang.org/p/Zad83sF-Dxv

structを使った実装

type originalError struct{ err error }

func (e originalError) Error() string { return e.err.Error() }

func main() {
    var err originalError
    as := errors.As(originalError{err: errors.New("1")}, &err)
    fmt.Printf("as = %v\n", as)
}

https://play.golang.org/p/WA-pdwXcM9W

注意点:pointerを意識してください

errors.As()の実装以外でもハマりがちなのが、pointerです。
下記を例にすると、originalError*originalError は違います。
よって、errors.As()falseを返します。

type originalError struct{ err error }

func (e originalError) Error() string { return e.err.Error() }

func main() {
    err := originalError{err: errors.New("1")}
    var target *originalError
    as := errors.As(err, &target)
    fmt.Printf("err = %T, target = %T\n", err, target)
    fmt.Printf("as = %v\n", as)
}

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

そして、当たり前なんですが、errの方をpointerにすれば、trueを返します。

type originalError struct{ err error }

func (e originalError) Error() string { return e.err.Error() }

func main() {
    err := &originalError{err: errors.New("1")} // pointer
    var target *originalError                   // pointer
    as := errors.As(err, &target)
    fmt.Printf("as = %v\n", as)
}

https://play.golang.org/p/2RHK2k7ZBJk

Wrapしたエラーとの比較に使えます

As()は値の差異は関係ないので、型があっているとtrueを返します。
なので、エラーが持っているメッセージは関係なく、型レベルで同じか確かめたいときに有効です!

type ErrorCode uint64

const (
    Zero ErrorCode = iota
    One
)

func (code ErrorCode) Error() string {
    return [...]string{
        "Error: Zero",
        "Error: One",
    }[code]
}
func main() {
    wrappedError := fmt.Errorf("wrap: %w", Zero)
    var code ErrorCode
    as := errors.As(wrappedError, &code)
    fmt.Printf("as = %v\n", as)
}

https://play.golang.org/p/9Sdw-th7znr

さいごに

結構詳しめにerrors.Is() errors.As()について調べて疲れましたw
ただ利用頻度が高いライブラリだと思うので、しっかりと抑えて今日学んだ知識を活かしていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go: panicキャッチ後にStackTraceを表示する

Panicをrecoverした後に、StackTraceを表示したかった

いろんなライブラリをあさって発見したのでメモ

package main

import(
    "runtime"
    "log"
)

func main() {

    defer func() {
        if err := recover(); err != nil {
            stackTrace()
        }
    }()


    panic("Panic Error!")
}

func stackTrace() {
    stack := make([]byte, 1024*8)
    stack = stack[:runtime.Stack(stack, false)]
    log.Println(string(stack))
}


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む