20200515のGoに関する記事は4件です。

【Golang】クロージャー

【Golang】クロージャー

Golangの基礎学習〜Webアプリケーション作成までの学習を終えたので、復習を兼ねてまとめていく。 基礎〜応用まで。

package main
//クロージャー
//変数に格納して、実行できる。

import "fmt"


//クロージャーによるジェネレーター
//関数を返す関数、関数を引数に取る関数(渡された関数をそのまま実行する)
//返り値でfunc() intを返す
func incrementGenerator() func() int {
    //変数宣言時のみ
    x := 0
    //ここまでがクロージャーで定義

    //funcを返す 2回目以降はここから
    return func() int {
        x++
        return x
    }
}

//クロージャーの使い方
func circleArea(pi float64) func(radius float64) float64 {
    return func(radius float64) float64 {
        return pi * radius * radius
    }
}

func main() {
    //counterはインナー関数になる
    counter := incrementGenerator()
    fmt.Println(counter())
    //>>1
    fmt.Println(counter())
    //>>2
    fmt.Println(counter())
    //>>3
    fmt.Println(counter())
    //>>4

    //変数に格納して実行
    //引数を先に指定
    //同じ関数で引数を動的にし、インナー関数の引数を動的に買えたい場合
    //pi=3.14
    c1 := circleArea(3.14)
    //c1はインナー関数になる
    //インナー関数の引数
    //radius=2
    fmt.Println(c1(2))
    fmt.Println(c1(3))

    //上記と同様
    c2 := circleArea(3)
    fmt.Println(c2(2))
    fmt.Println(c2(3))
}

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

【Golang】関数

【Golang】関数

Golangの基礎学習〜Webアプリケーション作成までの学習を終えたので、復習を兼ねてまとめていく。 基礎〜応用まで。

package main
//関数

import "fmt"
/*
//定義
func 関数名(引数 引数の型)戻り値型{

}
*/

//funcで関数宣言(引数の型)(返り値の型)
//同じ型の場合(x, y int)と省略できる。通常は(x int,y int)
//return で返す場合は返り値の型が必要。
func add(x, y int) (int, int) {
    return x + y, x - y
}

//ネームドリターンバリュー
//引数、返り値を見ただけで、何をするのかが分かるので推奨。
//reslut int で変数名と返り値の型指定
//最初に返り値を指定しているので、returnは省略できる
func cal(price, item int) (result int) {
    //返り値で指定しているので、:=にしなくてもいい
    result = price * item
    return
}

//戻り値のない関数
func noreturn(){
    fmt.Println("No Return")
    return
}


func convert(price int) float64 {
    return float64(price)
}

func main() {
    r1, r2 := add(10, 20)
    fmt.Println(r1, r2)

    //戻り値の破棄 _
    //r1, _ := add(10, 20)


    r3 := cal(100, 2)
    fmt.Println(r3)

    noreturn()

    a := convert(1)
    fmt.Println(a)


    //無名関数
    //オブジェクトに関数を格納
    f := func(x int) {
        fmt.Println("inner func", x)
    }
    f(1)

    //簡易的な関数ならそのまま実行する方法
    func(x int) {
        fmt.Println("inner func", x)
    }(1)
}

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

Goでリトライ可能エラーと致命的エラーを投げ分けて区別する試み

やりたいこと

Java系の言語だとtry-catchブロックで例外の種類ごとに例外処理を記述できます。例外が100種類あるんだけど、うち50種類はリトライ可能で、リトライ処理を一括で書きたい場合に以下のように書けるわけです。

try {
  throw new FooException(); // FooExceptionはRetryableExceptionを継承している
} catch (RetryableException e) {
  // リトライ処理
} catch (Exception e) {
  // 致命的エラーなので諦めて死ぬ
}

Go初心者の私はGoで同じことをやりたくなったんですが、ピンとくる方法がみつからなかったので試しに実装してみました。

エラーのwrapとerrors.Is()を使う方法(Go 1.13以降が必須)

私の実装を紹介します。

package main

import (
    "errors"
    "fmt"
)

var RetryableError = errors.New("Retrying...")

func main() {
    err := Foo()
    if errors.Is(err, RetryableError) {
        // リトライ処理
    } else {
        // 致命的エラーなので諦めて死ぬ
    }
}

func Foo() error {
    return fmt.Errorf("Foo is temporarily unavailable. %w", RetryableError)
}

リトライ可能エラーとそれ以外のエラーを区別したい場合に、リトライ可能エラーは必ずRetryableErrorをwrapするようにします。エラーを受け取った側はerrors.Is(err, RetryableError)でリトライ可能かどうか区別できます。

RetryableErrorのエラーメッセージは単体で見ると意味不明だと思いますが、wrapしたエラーを理解しやすくする意図です。fmt.Errorf()%wではwrap対象のエラーメッセージが展開されますので、Foo()が投げるエラーのエラーメッセージは次のようになります。

Foo is temporarily unavailable. Retrying...

これはログに出すのに便利なので個人的には気に入っています。また、リトライ回数上限に到達したような場合など、RetryableErrorを外すこともできます。

    err := Foo()
    if errors.Is(err, RetryableError) {
        retry--
        if retry >= 0 {
            log.Printf("Ignorable error: %+v\n", err)
            // リトライ処理
        }
        err = fmt.Errorf("%s Gave up.", err.Error()) // RetryableErrorを外す
    }

ご意見募集

正直なところ、これが良い方法なのかはわかっていません。Go上級者の方のご意見をお待ちしております。

参考文献

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

Goサーバーあれこれ mux

サーバー立ち上げ

Methodsでアクセスできるメソッドを指定できる。
mux.Vars(r)でurlパラメータを取得できる。

func main() {

    router := mux.NewRouter()
    router.HandleFunc("/book/{id}", handler).Methods(http.MethodGet)
    http.ListenAndServe(":8080", router)
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %q \n", html.EscapeString(r.URL.Path))

    //urlパラメータの取り出し
    vars := mux.Vars(r)
    id := vars["id"]
    fmt.Fprintf(w, id)
}

ミドルウェアの使用

基本的には下の書き方
これで/postにアクセスしようとすると、まずさきにミドルウェアに飛ばされて、その後、/postにいく。
ミドルウェアの処理によっては/postにはいかない。(loginしていないと/postにいけないとか)

router.Use(使用したいミドルウェア関数)
router.HandleFunc("/post",~~~)

ミドルウェア関数の書き方

func RequireAuthorization(al authz.AuthLayerInterface, role string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

//user認証失敗したら終了。成功ならnext.ServeHttpでハンドラーへみたいな処理
            if data.UserData.Role != role && data.UserData.Role != "admin" {
                handlers.Forbidden(w, r)
                return
            }

            next.ServeHTTP(w, r)
        })
    }
}

場合によってミドルウェアを使い分けたい場合は、下みたいに書くといいかも

    router := mux.NewRouter()

// 関係ない。 変数の定義。ミドルウェアとか、ハンドラーに渡せる。
    data := db.NewSQLDataStorage(dbConn)
    authLayer := authz.NewAuthLayer(&data)

//普通のハンドリング
    attachHandlers(router, &data, localFileStorage.FileStorage{}, authLayer)
//ミドルウェアに渡してからハンドリング
    attachReqAuth(router, authLayer)
//別のミドルウェアに渡してからハンドリング
    attachAdminRoutes(router, &data, authLayer

//普通のハンドリング
func attachHandlers(mux *mux.Router, storage db.DataStorage, fileStorage recordings.FileStorage, al authz.AuthLayerInterface) {
    sUtility := search.NewDatabaseSearchUtility(storage)
    mux.HandleFunc("/recordings/{id}", handlers.ServeRecordingFile(storage, fileStorage)).Methods(http.MethodGet)

}

//ミドルウェアに渡してからハンドリング
func attachReqAuth(mux *mux.Router, al authz.AuthLayerInterface) {
// pathPrefix("")はそこで指定したパス+ハンドルで指定したパスになる
// pathPrefix("/book")でHandleFunc("/post",~)なら/book/postが指定されたときになる(多分)
    muxAuth := mux.PathPrefix("").Subrouter()
    muxAuth.Use(middleware.RequireAuthorization(al, "user"))
    muxAuth.HandleFunc("/auth/logout", handlers.Logout(al)).Methods(http.MethodPost)
}

//別のミドルウェアに渡してからハンドリング
func attachAdminRoutes(mux *mux.Router, data db.DataStorage, al authz.AuthLayerInterface) {
    muxAdmin := mux.PathPrefix("").Subrouter()
    muxAdmin.Use(middleware.RequireAuthorization(al, "admin"))

    muxAdmin.HandleFunc("/books", handlers.CreateBook(data)).Methods(http.MethodPost)
    muxAdmin.HandleFunc("/recordings", handlers.CreateRecording(data)).Methods(http.MethodPost)
}

handlerに引数を渡す。

func main() {

    router := mux.NewRouter()

    param := "string param"
//paramを渡してハンドラーの中で使う
    router.HandleFunc("/get", getParam(param)).Methods(http.MethodGet)
    http.ListenAndServe(":8080", router)
}


func getParam(param string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, param)
    }

// error
    if err != nil {
        return
    }
}

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