LoginSignup
0
0

More than 1 year has passed since last update.

Go1.20の新機能を少し触ってみた

Last updated at Posted at 2023-03-23

今回触ってみた新機能はこちら

  • errors.Join()
  • context.WithContextCause()
  • reflect.Value.Comparable()
  • strings.CutPrefix()

errors.Join()

Join returns an error that wraps the given errors. Any nil error values are discarded. Join returns nil if errs contains no non-nil values. The error formats as the concatenation of the strings obtained by calling the Error method of each element of errs, with a newline between each string.

ふむふむ。引数に渡した複数のerrorをラップして改行で繋いでくれるみたいですね。
渡したerrorがnilの場合は見てみぬふりをするみたいです。

公式の例を引っ張ってきました。

errors.Join()で複数エラーをラップ
package main

import (
	"errors"
	"fmt"
)

func main() {
	err1 := errors.New("err1")
	err2 := errors.New("err2")
	err := errors.Join(err1, err2)
	fmt.Println(err)
	if errors.Is(err, err1) {
		fmt.Println("err is err1")
	}
	if errors.Is(err, err2) {
		fmt.Println("err is err2")
	}
}

結果

err1
err2
err is err1
err is err2

確かに改行で連結されていて、errors.Is()でも等価として判定されています。
しかしfmt.Errorf()と違って、wrapError型ではなく普通のerror型なので、errors.Unwrap()でもとのerrorを取り出すことはできなさそうです。

ちなみにfmt.Errorf()は今までは1つのエラーしかラップできませんでしたが、1.20で複数のエラーをラップできるようになったみたいです。

fmt.Errorfで複数エラーをラップ
package main

import (
	"errors"
	"fmt"
)

func main() {
	e1 := errors.New("error1")
	e2 := errors.New("error2")

	err := fmt.Errorf("errors: %w, %w", e1, e2)
	fmt.Println(err)
}

結果(Go1.19以前)

errors: error1, %!w(*errors.errorString=&{error2})

結果(Go1.20)

errors: error1, error2

errors.Join()はerrorを改行で繋ぐだけでエラーの詳細情報の付与とかはできないので、fmt.Errorf()の劣化版なのでは?としか読み取れませんでした。。

context.WithCancelCause()

WithCancelCause behaves like WithCancel but returns a CancelCauseFunc instead of a CancelFunc. Calling cancel with a non-nil error (the "cause") records that error in ctx; it can then be retrieved using Cause(ctx). Calling cancel with nil sets the cause to Canceled.

WithCancel()と似ているけど、CancelFuncではなくCancelCauseFuncを返すみたいです。
今までエラーの内容はctx.Err()でCanceledエラーかDeadlineExceededエラーを取得していましたが、context.WithCancelCause()はそれに加えてCancelCauseFunc()に独自エラーを渡せばcontext.Cause()でそれを取得できるのでエラーの表現の幅が増えますね。

playground

package main

import (
	"context"
	"fmt"
	"time"
)

type myError struct{}

func (e *myError) Error() string {
	return "DBに接続できませんでした"
}

func fn(ctx context.Context) {
	fmt.Println("start")
	defer fmt.Println("finish")
	for i := 1; i <= 5; i++ {
		select {
		case <-ctx.Done():
			fmt.Println(context.Cause(ctx))
			return
		default:
			fmt.Printf("%ds\n", i)
			time.Sleep(1 * time.Second)
		}
	}
}

func main() {
	e := &myError{}
	ctx, cancel := context.WithCancelCause(context.Background())
	go fn(ctx)
	time.Sleep(2 * time.Second)
	cancel(e)
	time.Sleep(3 * time.Second)
}

ちなみにWithDeadlineCause()WithTimeoutCause()もありました。

reflect.Value.Comparable()

Comparable reports whether the value v is comparable. If the type of v is an interface, this checks the dynamic type.

reflectパッケージのValue型が比較可能な値なのかを判定してくれます。
比較可能な値とはintやstring、同じフィールドを持つ構造体、インターフェースなどですね。逆に比較できないのはスライスやマップ、関数などです。
比較可能性についての記事

playground

package main

import (
	"fmt"
	"reflect"
)

func main() {
	num := 1
	fmt.Println(reflect.ValueOf(num).Comparable())

	type User struct {
		ID   int
		Name string
	}
	u := &User{1, "hoge"}
	fmt.Println(reflect.ValueOf(u).Comparable())

	f := func() {}
	fmt.Println(reflect.ValueOf(f).Comparable())
}

strings.Cutprefix()

CutPrefix returns s without the provided leading prefix string and reports whether it found the prefix. If s doesn't start with prefix, CutPrefix returns s, false. If prefix is the empty string, CutPrefix returns s, true.

第二引数の文字列が第一引数の文字列の先頭に存在した場合、それを取り除いた文字列とtrueを返します(存在しなければfalse)。
strings.TrimPrefixも似たような挙動ですが、こちらは指定されたプレフィッ
クスが見つかったかどうかのboolは返ってきません。

playground

package main

import (
	"fmt"
	"strings"
)

func main() {
	s, ok := strings.CutPrefix("Hello,World!", "Hello,")
	fmt.Println(s)
	fmt.Println(ok)

	s, ok = strings.CutPrefix("Hello,World!", "こんにちは、")
	fmt.Println(s)
	fmt.Println(ok)
}


ちなみにbytesパッケージにも同様の関数が存在し、末尾を削除するCutSuffix()もそれぞれ追加されたみたいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0