0
0

Goのスライスの定義で少しハマったこと

Posted at

かなり初歩的な理解不足だったと思うのですが、忘れないために備忘録として残します。

TL;DR

スライスの宣言だけと初期化は厳密には違い、nil sliceと空sliceの挙動が違うよという話

何をしてしまったのか

以下のようなことを実現するつもりでした。

  • 特定のスライスとスライスを組み合わせて、一つの変数(hogeList)にしてレスポンスを返す
  • 空だった場合は{"List":[]}でjsonで返却する
    echoを使った簡単なイメージは以下の通りです。
package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
)

type (
	Hoge struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	HogeResponse struct {
		HogeList []Hoge `json:"hogeList"`
	}
)

func main() {
	e := echo.New()
	e.GET("/api/hoge", func(c echo.Context) error {
		hogeList := HogeResponse{
			HogeList: []Hoge{},
		}

		return c.JSON(http.StatusOK, hogeList)
	})

	e.Logger.Fatal(e.Start(":8080"))
}

これはHogeResponseの変数を定義してHogeListも空として定義したので
想定通り空リストが返却されました。

{
    "hogeList": []
}

例えばこれにデータを入れて返すとします。
先ほどのコードを少しいじりました。

package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
)

type (
	Hoge struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	HogeResponse struct {
		HogeList []Hoge `json:"hogeList"`
	}

	// 取得した加工したいデータとする
	FugaData struct {
		ID      int    `json:"id"`
		Name    string `json:"name"`
		Place   string `json:"place"`
		Address string `json:"address"`
	}
	FugaList []FugaData
)

func main() {
	e := echo.New()
	e.GET("/api/hoge", func(c echo.Context) error {
		hogeList := HogeResponse{
			HogeList: []Hoge{},
		}

		// データを取得したとします
		data := []FugaData{
			{ID: 1, Name: "hoge1", Place: "tokyo", Address: "tokyo"},
			{ID: 2, Name: "hoge2", Place: "longdon", Address: "longdon"},
			{ID: 3, Name: "hoge3", Place: "paris", Address: "paris"},
		}

		// データをhogeとして格納する
		var ret []Hoge
		for _, d := range data {
			ret = append(ret, Hoge{
				ID:   d.ID,
				Name: d.Name,
			})
		}
		// 格納したhogeを元のデータに格納する
		hogeList.HogeList = ret

		return c.JSON(http.StatusOK, hogeList)
	})

	e.Logger.Fatal(e.Start(":8080"))
}

これを動かすと以下のようにデータが取得できます。まあ普通ですね。

{
	"hogeList": [
		{
			"id": 1,
			"name": "hoge1"
		},
		{
			"id": 2,
			"name": "hoge2"
		},
		{
			"id": 3,
			"name": "hoge3"
		}
	]
}

で、問題はここでした。
ケースによっては、fugaデータがない場合があり、その場合は想定通り
Listは空のリストで返却したいです。
以下のように変更してみました。

package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
)

type (
	Hoge struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	HogeResponse struct {
		HogeList []Hoge `json:"hogeList"`
	}

	// 取得した加工したいデータとする
	FugaData struct {
		ID      int    `json:"id"`
		Name    string `json:"name"`
		Place   string `json:"place"`
		Address string `json:"address"`
	}
	FugaList []FugaData
)

func main() {
	e := echo.New()
	e.GET("/api/hoge", func(c echo.Context) error {
		hogeList := HogeResponse{
			HogeList: []Hoge{},
		}

		// データはありません
		data := []FugaData{}

		// データをhogeとして格納する
		var ret []Hoge

		// 回すデータはないので、このループは空振ります
		for _, d := range data {
			ret = append(ret, Hoge{
				ID:   d.ID,
				Name: d.Name,
			})
		}
		// 格納したhogeを元のデータに格納する
		hogeList.HogeList = ret

		return c.JSON(http.StatusOK, hogeList)
	})

	e.Logger.Fatal(e.Start(":8080"))
}

とループが回らないように変更するとレスポンスが変わりました。

{
    "hogeList": null
}

おおぅ..と思いました。
てっきり、var ret []Hogeとしていたので、このretは少なくともスライスが定義されていると
考えていました。
(完全にPHP脳でした)

どうやらスライスの変数は、宣言したか・初期化したかで挙動が変わってしまうようです。
今回のケースは宣言だけだったのでnil sliceとなっていたようでした。
空スライスに変更してみます。

package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
)

type (
	Hoge struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	HogeResponse struct {
		HogeList []Hoge `json:"hogeList"`
	}

	// 取得した加工したいデータとする
	FugaData struct {
		ID      int    `json:"id"`
		Name    string `json:"name"`
		Place   string `json:"place"`
		Address string `json:"address"`
	}
	FugaList []FugaData
)

func main() {
	e := echo.New()
	e.GET("/api/hoge", func(c echo.Context) error {
		hogeList := HogeResponse{
			HogeList: []Hoge{},
		}

		// データはありません
		data := []FugaData{}

		// データをhogeとして格納する
		ret := []Hoge{} // 初期化に変更

		// 回すデータはないので、このループは空振ります
		for _, d := range data {
			ret = append(ret, Hoge{
				ID:   d.ID,
				Name: d.Name,
			})
		}
		// 格納したhogeを元のデータに格納する
		hogeList.HogeList = ret

		return c.JSON(http.StatusOK, hogeList)
	})

	e.Logger.Fatal(e.Start(":8080"))
}
{
    "hogeList": []
}

すごい初歩的なことですが、あまり意識して使ってなかったなと反省しました。

参考

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