20200214のGoに関する記事は10件です。

Golang vs. Python – Is Golang Better Than Python?

In this tutorial we are going to make the comparison between Golang or Python, you will have the complete idea at the end of this tutorial which programming language between Python or Goland you should opt for your next software development project.

click here to read more
https://www.positronx.io/golang-vs-python-is-golang-better-than-python/

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

【Hyperledger Fabric v2.0】PrivateDataを使ってみよう

はじめに

あけましておめでとうございます(ぇ
どーも、のぶこふです。

ICE BREAK

日本時間 2020/01/30(木)7:00頃 に HLF v2.0 が正式リリースされましたね。
私はGithubに張り付いていたのでリリースされた瞬間にTwitterで発信しましたが、
いかんせん私のフォロワーが少ないので、拡散するには力が及びませんでした。

とまぁ、そんなことは置いておいて、
HLF v2.0 の目玉機能といえば
「Private Data」ですね(たぶん
※どんな変更点があるか等は、他の方がまとめてくださっているので、
 そちらをご参考願います。ワタシ エイゴ シャベレマセーン
 ▼コチラとか
 https://gakumura.hatenablog.com/entry/2020/01/30/180540

「おいおい、のぶこふさんよぉ。Private Dataなら、昔からあったぜぇ?」
はい、そうです。ありました。
なんなら、使い方をまとめた記事も書きました。

▼【Hyperledger Fabric】PrivateDataを使ってみよう
https://qiita.com/nobkovskii/items/a46160d1c1ba80d0fdb7

v2.0になり、よりお手軽にPrivate Dataを扱えるようになりました。
HLFがPrivate Data推しになり、
なんとなくCordaっぽくなり始めてる感があります。
もう、わけわからんです。

閑話休題

ということで、今回は「v2.0でPrivate Dataを扱う」に焦点を当てて説明していきます。
v1.4系なら、マーブルにサンプルがあるのですが、
v2.0系は、まだサンプルが公式には出ていないので、参考になればと思います。
今回も、おなじみのFabcarをいじくり回します。
※なお、修正内容については、前回記事と同様です。

想定読者・ゴール・環境

  • 想定読者
    • HLFのPrivateDataを使いたい人
  • ゴール
    • HLF v2.0 でPrivateDataが実装できるようになる
  • 環境
    • HLF v2.0
    • CentOS7 On Oracle VM VirtualBox On Windows10
    • Chaincode:Golang

つくる

サンプルダウンロード

おなじみのサンプルを使用します。
任意のディレクトリで実行してください。

いつものやつをダウンロード
// get hlf-sample
# curl -sSL https://bit.ly/2ysbOFE | bash -s

【不要】collections_config.jsonを作成

v2.0 では、collections_config.jsonは不要になりました。
詳しい説明は公式ドキュメントをご参照ください。

ただし、Private Data の挙動等を設定する場合には、必要になります。
ケースバイケースで、要否に応じて、作成する/しないを決めれば良いと思います。

▼公式ドキュメント
https://hyperledger-fabric.readthedocs.io/en/latest/private-data-arch.html#referencing-implicit-collections-from-chaincode

Chaincode修正

PrivateDataに保存、取得ができるように、CCを修正します。

fabcar.go

fabcar.go:新しく構造体を作成します
// Private Data Collection
type CarPrivate struct {
        Make   string `json:"make"`
        Model  string `json:"model"`
        Colour string `json:"colour"`
        Owner  string `json:"owner"`
        Price  string `json:"price"`
}
適当に定数を作成しておきます
const COLLECTIONS="_implicit_org_Org1MSP"
fabcar.go:PrivateDataを保存する関数を作成します
func (s *SmartContract) CreatePrivateCar(ctx contractapi.TransactionContextInterface, carNumber string, make string, model string, colour string, owner string, price string) error {
        car := CarPrivate{
                Make:   make,
                Model:  model,
                Colour: colour,
                Owner:  owner,
                Price:  price,
        }

        carAsBytes, _ := json.Marshal(car)

        return ctx.GetStub().PutPrivateData(COLLECTIONS, carNumber, carAsBytes)
}
fabcar.go:PrivateDataを取得する関数を作成します
func (s *SmartContract) QueryPrivateCar(ctx contractapi.TransactionContextInterface, carNumber string) (*CarPrivate, error) {
        carAsBytes, err := ctx.GetStub().GetPrivateData(COLLECTIONS,carNumber)

        if err != nil {
                return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
        }

        if carAsBytes == nil {
                return nil, fmt.Errorf("%s does not exist", carNumber)
        }

        car := new(CarPrivate)
        _ = json.Unmarshal(carAsBytes, car)

        return car, nil
}

【不要】インスタンス化時にcollections_config.jsonを読み込むようにする

読み込みも不要になりました。
そもそも、v2.0ではインスタンス化の概念がなくなりましたね。

collections_config.jsonを読み込む場合は、Chaincodeを「approveformyorg」「commit」する際に「--collections-config」と「collections_config.jsonのパス」を指定します。

Chaincode呼び出し元アプリ作成

作成したChaincodeを呼び出すアプリを作成(コピーして修正)します。

cp invoke.js privateInvoke.js
cp query.js privateQuery.js
privateInvoke.js
/*
 * SPDX-License-Identifier: Apache-2.0
 */

'use strict';

const { Gateway, Wallets } = require('fabric-network');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');

async function main() {
    try {

        // Create a new file system based wallet for managing identities.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = await Wallets.newFileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // Check to see if we've already enrolled the user.
        const identity = await wallet.get('user1');
        if (!identity) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

        // Create a new gateway for connecting to our peer node.
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });

        // Get the network (channel) our contract is deployed to.
        const network = await gateway.getNetwork('mychannel');

        // Get the contract from the network.
        const contract = network.getContract('fabcar');

        // Submit the specified transaction.
        // <<<<<<<<<<<<<<<<  ADD  >>>>>>>>>>>>>>>
        await contract.submitTransaction('createPrivateCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom', '1000');
        console.log('Transaction has been submitted');

        // Disconnect from the gateway.
        await gateway.disconnect();

    } catch (error) {
        console.error(`Failed to submit transaction: ${error}`);
        process.exit(1);
    }
}

main();
privateQuery.js
/*
 * SPDX-License-Identifier: Apache-2.0
 */

'use strict';

const { Gateway, Wallets } = require('fabric-network');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');

async function main() {
    try {

        // Create a new file system based wallet for managing identities.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = await Wallets.newFileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // Check to see if we've already enrolled the user.
        const identity = await wallet.get('user1');
        if (!identity) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

        // Create a new gateway for connecting to our peer node.
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });

        // Get the network (channel) our contract is deployed to.
        const network = await gateway.getNetwork('mychannel');

        // Get the contract from the network.
        const contract = network.getContract('fabcar');

        // Evaluate the specified transaction.
        // <<<<<<<<<<<<<<<<  ADD  >>>>>>>>>>>>>>>
        const result = await contract.evaluateTransaction('queryPrivateCar', 'CAR12');
        console.log(`Transaction has been evaluated, result is: ${result.toString()}`);

    } catch (error) {
        console.error(`Failed to evaluate transaction: ${error}`);
        process.exit(1);
    }
}

main();

動かす

startFabric.sh 実行

// change directory
# cd fabric-sample/fabcar

// Start HLF
# ./startFabric

admin & user 作成

// change directory
# cd javascript

// npm install
# npm install

// enroll admin & register user
# node enrollAdmin.js
# node registerUser.js

PrivateData 取得 → 登録 → 取得

// query ledger
# node privateQuery.js
warn: [Query]: evaluate: Query ID "[object Object]" of peer "peer0.org1.example.com:7051" failed: message=CAR12 does not exist, stack=Error: CAR12 does not exist
// ↑ Private Data に未登録のため、Warningが発生。

// invoke ledger
# node privateInvoke.js
Transaction has been submitted
// ↑ 無事に登録

// query ledger
# node privateQuery.js
Transaction has been evaluated, result is: {"make":"Honda","model":"Accord","colour":"Black","owner":"Tom","price":"1000"}
// ↑ 登録した内容が表示された

StateDB 取得

# node query.js
Transaction has been evaluated, result is: [{"Key":"CAR0","Record":{"make":"Toyota","model":"Prius","colour":"blue","owner":"Tomoko"}},{"Key":"CAR1","Record":{"make":"Ford","model":"Mustang","colour":"red","owner":"Brad"}},{"Key":"CAR2","Record":{"make":"Hyundai","model":"Tucson","colour":"green","owner":"Jin Soo"}},{"Key":"CAR3","Record":{"make":"Volkswagen","model":"Passat","colour":"yellow","owner":"Max"}},{"Key":"CAR4","Record":{"make":"Tesla","model":"S","colour":"black","owner":"Adriana"}},{"Key":"CAR5","Record":{"make":"Peugeot","model":"205","colour":"purple","owner":"Michel"}},{"Key":"CAR6","Record":{"make":"Chery","model":"S22L","colour":"white","owner":"Aarav"}},{"Key":"CAR7","Record":{"make":"Fiat","model":"Punto","colour":"violet","owner":"Pari"}},{"Key":"CAR8","Record":{"make":"Tata","model":"Nano","colour":"indigo","owner":"Valeria"}},{"Key":"CAR9","Record":{"make":"Holden","model":"Barina","colour":"brown","owner":"Shotaro"}}]
// ↑ Private Data に登録した「"Key":"CAR12"」は無い

別Org(Org2)で確認

今回、Private Data を登録したのは、Org1です。
Org2にはアクセス権がありません。
挙動を確認してみましょう。

# docker exec -it cli sh
 - cliコンテナに入ります
 - 以下、cliコンテナ内

// Org2 用の環境変数設定
CHANNEL_NAME=mychannel
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:9051

// 実行
peer chaincode query -C $CHANNEL_NAME -n fabcar -c '{"Args":["queryPrivateCar","CAR12"]}'
Error: endorsement failure during query. response: status:500 message:"Failed to read from world state. GET_STATE failed: transaction ID: 0d650b361164a8f69aef1e0cae6092ec1ea7292c93cff8d4d7ca34540d0541c8: private data matching public hash version is not available. Public hash version = {BlockNum: 7, TxNum: 0}, Private data version = <nil>"
// ↑ エラーが返ってきました

正常Org(Org1)で確認

続いて、Org1でも確認してみます。
cliのコンテナで実施します。

// Org1 用の環境変数設定
CHANNEL_NAME=mychannel
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051

// 実行
peer chaincode query -C $CHANNEL_NAME -n fabcar -c '{"Args":["queryPrivateCar","CAR12"]}'
{"make":"Honda","model":"Accord","colour":"Black","owner":"Tom","price":"1000"}
// 登録した内容が表示された

宿題

collections_config.json を使用してみる

HLFv2.0から、collections_config.jsonを使用しなくてもPrivate Dataを扱えるようになりました。
しかし、Private Data を柔軟に扱うためには、collections_config.jsonはかかせません。たぶん。

だけど、ちょっと調べてみると、"_implicit_org_Org1MSP"を使ってしまうと、collections_config.jsonは参照されなくなるっぽいな・・・
ここらへん、どうしたら良いんだろ?
調査続けます。

終わりに

以前(v1.4系)よりも、よりお手軽にPrivate Dataを扱うことができるようになりました。
ますますPrivate Dataの使用が推進されそうですね。

今回はここまでです。
ありがとうございました。

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

Go言語でnewとmakeを使ってみた

はじめに

Go言語でnewとmakeを使い、違いを備忘のために記します。

環境

macOS Catalina 10.15.3
go1.13.3 darwin/amd64

1. makeを使う

以下はmakeを使用して、変数を初期化する例です。コードは一部抜粋します。

sample.go
s := make([]int, 0)
m := make(map[string]int)
ch := make(chan int)

fmt.Printf("%T\n", s)
fmt.Printf("%T\n", m)
fmt.Printf("%T\n", ch)

[出力結果]

[]int
map[string]int
chan int

2. newを使う

以下はnewを使用して、変数を初期化する例です。コードは一部抜粋します。

sample.go
var p *string = new(string)
var str = new(struct{})

fmt.Printf("%T\n", p)
fmt.Printf("%T\n", str)

[出力結果]

*string
*struct{}

結論

newで変数を宣言すると値が返り、makeで変数を宣言するとポインタが返りました。
newとmakeの違いを問われて答えられなかったので、記事書いて頭に刻み込みます:sweat:

以上!

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

Go Ahead Eagles vs FC Utrecht Fußball Live

Go Ahead Eagles vs FC Utrecht live: Dutch KNVB Beker 2020 Match starts at around 19:45 uk time with and BT Sports will have live coverage in the UK while NBCSN will broadcast the game live in the US. Links with tick mark at the end are already checked and working. (most starts closer to kickoff)

GO LIVE NOW: Go Ahead Eagles vs FC Utrecht live

WaTch HERE

Go Ahead Eagles vs FC Utrecht live stream free

FC Utrecht vs Go Ahead Eagles live

Last Saturday, they fell a goal short of FC Utrecht , losing 1–2. This makes it the second loss in a row for FC Utrecht .

FC Utrecht left their first contest against FC Utrecht this season with a spring in their step. FC Utrecht dodged a bullet, finishing off FC Utrecht 1–0. FC Utrecht can consider this payback for the 0–1 defeat they dealt FC Utrecht the last time the teams encountered one another.

FC Utrecht ’s victory lifted them to 1–0–1 (four points) while FC Utrecht ’s loss dropped them down to 0–2 (zero points). We’ll see if FC Utrecht can repeat their recent success or if newcomer FC Utrecht bounce back and reverse their fortune.

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

FC Utrecht vs Go Ahead Eagles - live stream

Go Ahead Eagles vs FC Utrecht live: Dutch KNVB Beker 2020 Match starts at around 19:45 uk time with and BT Sports will have live coverage in the UK while NBCSN will broadcast the game live in the US. Links with tick mark at the end are already checked and working. (most starts closer to kickoff)

GO LIVE NOW: Go Ahead Eagles vs FC Utrecht live

WaTch HERE

Go Ahead Eagles vs FC Utrecht live stream free

FC Utrecht vs Go Ahead Eagles live

Last Saturday, they fell a goal short of FC Utrecht , losing 1–2. This makes it the second loss in a row for FC Utrecht .

FC Utrecht left their first contest against FC Utrecht this season with a spring in their step. FC Utrecht dodged a bullet, finishing off FC Utrecht 1–0. FC Utrecht can consider this payback for the 0–1 defeat they dealt FC Utrecht the last time the teams encountered one another.

FC Utrecht ’s victory lifted them to 1–0–1 (four points) while FC Utrecht ’s loss dropped them down to 0–2 (zero points). We’ll see if FC Utrecht can repeat their recent success or if newcomer FC Utrecht bounce back and reverse their fortune.

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

Go Ahead Eagles vs Utrecht Live Score and Live Stream

Go Ahead Eagles vs FC Utrecht live: Dutch KNVB Beker 2020 Match starts at around 19:45 uk time with and BT Sports will have live coverage in the UK while NBCSN will broadcast the game live in the US. Links with tick mark at the end are already checked and working. (most starts closer to kickoff)

GO LIVE NOW: Go Ahead Eagles vs FC Utrecht live

WaTch HERE

Go Ahead Eagles vs FC Utrecht live stream free

FC Utrecht vs Go Ahead Eagles live

Last Saturday, they fell a goal short of FC Utrecht , losing 1–2. This makes it the second loss in a row for FC Utrecht .

FC Utrecht left their first contest against FC Utrecht this season with a spring in their step. FC Utrecht dodged a bullet, finishing off FC Utrecht 1–0. FC Utrecht can consider this payback for the 0–1 defeat they dealt FC Utrecht the last time the teams encountered one another.

FC Utrecht ’s victory lifted them to 1–0–1 (four points) while FC Utrecht ’s loss dropped them down to 0–2 (zero points). We’ll see if FC Utrecht can repeat their recent success or if newcomer FC Utrecht bounce back and reverse their fortune.

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

LIVE>> Go Ahead Eagles - Utrecht online leben

Go Ahead Eagles vs FC Utrecht live: Dutch KNVB Beker 2020 Match starts at around 19:45 uk time with and BT Sports will have live coverage in the UK while NBCSN will broadcast the game live in the US. Links with tick mark at the end are already checked and working. (most starts closer to kickoff)

GO LIVE NOW: Go Ahead Eagles vs FC Utrecht live

WaTch HERE

Go Ahead Eagles vs FC Utrecht live stream free

FC Utrecht vs Go Ahead Eagles live

Last Saturday, they fell a goal short of FC Utrecht , losing 1–2. This makes it the second loss in a row for FC Utrecht .

FC Utrecht left their first contest against FC Utrecht this season with a spring in their step. FC Utrecht dodged a bullet, finishing off FC Utrecht 1–0. FC Utrecht can consider this payback for the 0–1 defeat they dealt FC Utrecht the last time the teams encountered one another.

FC Utrecht ’s victory lifted them to 1–0–1 (four points) while FC Utrecht ’s loss dropped them down to 0–2 (zero points). We’ll see if FC Utrecht can repeat their recent success or if newcomer FC Utrecht bounce back and reverse their fortune.

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

アプリ実装者のためのGo1.13からのエラーとの付き合い方

はじめに

Go1.13から機能追加されたエラーに関してキャッチアップがまだできていないアプリケーション開発者のための記事です。

TL;DR

アプリケーション実装者は以下をすることで嬉しさを得られます。

  • fmt.Errorf("%w", err) または Unwrap() errorを実装したエラー型をカスタムエラーにする。
  • errors.Is関数、errors.As関数を使いエラーハンドリングの受け口を実装する。

Go1.12までのバージョンとの実装方法の比較はGo 1.13時代のエラー実装者のお作法の記事が非常に参考になります。

環境

$ go version
go version go1.13.1 darwin/amd64

ライブラリが提供するエラーとの付き合い方

ライブラリが返すエラー値をfmt.Errorf("%w", err)でラップすればerrors.Is関数で中身のライブラリ側エラー値を判定できる

以下のコードをあるGoのライブラリが提供するものだとします。

lib.go
package main // 本来は package lib な形式

import (
    "errors"
    "time"
)

var (
    ErrParse = errors.New("lib: parse error")
)

type LibTime time.Time

func ParseLibFunc(text string) (LibTime, error) {
    t, err := time.Parse(time.RFC3339, text)
    if err != nil {
        return LibTime{}, ErrParse // ライブラリが持つエラー型の値を返す
    }
    return LibTime(t), nil
}

このライブラリの関数ParseLibFuncを利用するアプリケーションを作っているとします。

ここではserviceというアプリの関数がライブラリ関数を呼び、ライブラリ関数から返ったエラーをGo1.13からのfmt.Errorf("%w", err)の記法でラップしながらエラーを呼び元であるmain関数に返します。ここではmain関数はアプリコードのエラーハンドリングを担当する関数とします。

main.go
package main

import (
    "errors"
    "fmt"

    // "XXX/User/lib" // 本来はこのようにライブラリをインポートする
)

func service(text string) error {
    _, err := ParseLibFunc(text)
    if err != nil {
        return fmt.Errorf("service error with '%w'", err) // Unwrapを実装したエラーを返す
    }
    return nil
}

func main() {
    if err := service("2020/02/14"); err != nil {
        // errors.Is 関数を使ってライブラリ提供のエラーかを判定
        if errors.Is(err, ErrParse) {
            // handle error
        }

        // 以下検証用

        fmt.Println(err) // service error with 'lib: parse error'

        // errors.Unwrap 関数でライブラリ関数を取り出す()
        wrappedErr := errors.Unwrap(err)
        fmt.Println(wrappedErr) // lib: parse error

        fmt.Println(errors.Is(wrappedErr, ErrParse)) // true
        fmt.Println(errors.Is(err, ErrParse))        // true
    }
}

service関数で新たなエラー型の値にしていますがfmt.Errorf("%w", err)とラップしているので、errors.Is関数を使うことでライブラリのエラー値の判定が可能になっています。

また、errors.Unwrap関数によりラップされた1つ分内側のエラー値を得ることができます。errors.Is関数は内部でerrors.Unwrapを繰り返し呼び出すことで判定を実現しています。

ライブラリが独自の公開エラー型の値を返すときは、ラップしてもerrors.As関数で本来のエラー値を得られる

lib.go
package main // 本来は package lib な形式

import (
    "errors"
    "time"
)

// LibError はライブラリの独自エラー型
type LibError struct {
    kind     string
    orgError error
}

func (l *LibError) Error() string {
    return "error occured in Lib"
}

func (l *LibError) Kind() string {
    return l.kind
}

type LibTime time.Time

func ParseLibFunc(text string) (LibTime, error) {
    t, err := time.Parse(time.RFC3339, text)
    if err != nil {
        return LibTime{}, &LibError{kind: "Parse", orgError: err} // 独自エラー型の値を返す
    }
    return LibTime(t), nil
}
main.go
package main

import (
    "errors"
    "fmt"
)

func service(text string) error {
    _, err := ParseLibFunc(text)
    if err != nil {
        return fmt.Errorf("service error with '%w'", err) // Unwrapを実装したエラーを返す
    }
    fmt.Println("service finished successfully")
    return err
}

func main() {
    if err := service("2020/02/14"); err != nil {
        var e *LibError
        // errors.As 関数を使うことでライブラリの独自エラー型の本来の値を得ることができる
        if errors.As(err, &e) {
            fmt.Println(e)        // error occurred in Lib
            fmt.Println(e.Kind()) // Parse
        }
    }
}

errors.As関数を使うことでライブラリ独自エラー型の値が実装するKind()メソッドを実行することができています。

アプリコード内のカスタムエラーはUnwrap() errorを実装しよう

上記の例ではservice関数はfmt.Errorf("%w", err)でUnwrapが実装されたエラー値を返していましたがアプリ内での独自エラー型を実装する場合はGo1.13時代以降はUnwrapメソッドを実装することが理想になります。

main.goの一部
// AppError はアプリコード側の独自エラー型
type AppError struct {
    orgErr error
    code   string
}

func (e AppError) Error() string {
    return fmt.Sprintf("code: %s, msg: app error occurred", e.code)
}

func (e AppError) Unwrap() error {
    return e.orgErr
}

func service(text string) error {
    _, err := ParseLibFunc(text)
    if err != nil {
        return AppError{orgErr: err, code: "00A"}
    }
    return err
}

func main() {
    if err := service("2020/02/14"); err != nil {
        fmt.Printf("%T\n", err)           // main.AppError
        fmt.Println(err)                  // code: 00A, msg: app error occurred
        wrappedErr := errors.Unwrap(err)
        fmt.Printf("%T\n", err)           // *main.LibError
        fmt.Println(wrappedErr)           // error occured in Lib

        fmt.Println(errors.Is(err, ErrParse)) // true
}

上記のようにUnwrap() errorのメソッドを実装すればアプリケーションのエラーハンドリングにてIs関数とAs関数をライブラリ提供のエラー値に対して適用することができます。

参考

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

【Go】配列のサンプルメモ

package main

import (
    "fmt"
)

func main() {
    // 配列の型
    // [長さ] 要素型
    // [長さ] 要素型 {要素の初期値..}

    // 配列の宣言
    var arr1 [2]string
    arr1[0] = "a"
    arr1[1] = "b"
    fmt.Println(arr1)

    // 配列初期化
    arr2 := [2]string{"a", "b"}
    fmt.Println(arr2)

    // https://yttm-work.jp/lang/go/go_0005.html#head_line_03
    var arr3 [2]string = [2]string{"a", "b"}
    fmt.Println(arr3)

    // 初期値で指定した要素の数が自動的に長さとなる
    arr4 := [...]string{"a", "b"}
    fmt.Println(arr4)
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Go】スライスのサンプルメモ

package main

import "fmt"

func main() {
    // 配列
    arr := [3]string{"a", "b", "c"}

    // スライス型の変数を宣言
    var s1 []string
    // スライス
    s1 = arr[:]
    fmt.Println(s1) // [a b c]

    // var を使わないVersion
    s2 := arr[:]
    fmt.Println(s2) // [a b c]

    // インデックスを指定
    s3 := arr[1:2]
    fmt.Println(s3) // [b c]

    s4 := arr[1:3]
    fmt.Println(s4) // [b c]

    // append
    s5 := append(s4, "d", "e")
    fmt.Println(s4) // [b c]
    fmt.Println(s5) // [b c d e]

    // 配列とスライスを同時に宣言
    s6 := []string{"A", "B", "C"}
    fmt.Println(s6)

    // 長さ、キャパシティ
    fmt.Println(len(s6)) // 3
    fmt.Println(cap(s6)) // 3

    s7 := append(s6, "D")
    fmt.Println(len(s7)) // 4
    fmt.Println(cap(s7)) // 6 --> 倍になった

    a8 := [...]int{0, 1, 2, 3, 4}
    s8 := a8[1:4]
    fmt.Println(s8) // [1 2 3]
    fmt.Println(len(s8)) // 3
    fmt.Println(cap(s8)) // 4

    s9 := a8[1:2]
    fmt.Println(len(s9)) // 1
    fmt.Println(cap(s9)) // 4

    s10 := a8[0:5]
    fmt.Println(len(s10)) // 5
    fmt.Println(cap(s10)) // 5
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む