質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

2回答

185閲覧

GOで変数の代入時の型について

yaeyama

総合スコア57

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

1グッド

1クリップ

投稿2019/04/07 09:37

編集2019/04/08 00:07

前提・実現したいこと

こちらを参考にGOとCleanArchitectureを勉強中です。
interfaces/controllers/user_controller.go

go

1func NewUserController(sqlHandler database.SQLHandler) *UserController { 2 return &UserController{ 3 Interactor: usecase.UserInteractor{ 4 UserRepository: &database.UserRepository{ 5 SQLHandler: sqlHandler, 6 }, 7 }, 8 } 9}

UserRepository に参照型の &database.UserRepository を入れているのはなぜなのでしょうか?

usecase/user_interactor.go

go

1type UserInteractor struct { 2 UserRepository UserRepository 3}

としているので値型で問題ないはずではないでしょうか?

補足情報(FW/ツールのバージョンなど)

$ go version go version go1.12.2 linux/amd64
flightkasai7👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2019/04/07 22:30

エラーメッセージは重要な情報を提供しています。省略することは理解から遠ざかることになるのでできるだけ「エラーになります」だけでなくエラーメッセージそのものを貼るようにしてください。
guest

回答2

0

自己解決

投稿2019/04/09 06:31

yaeyama

総合スコア57

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2019/04/09 08:15

さすが。ちゃんとエラーメッセージが貼ってありますね!素晴らしい。
guest

0

以下の2つの型は異なるものです。

  • database.UserRepository
  • usecase.UserRepository

前者はstruct、後者はinterfaceです。
またdatabase.UserRepositoryのメソッド群はレシーバがポインタ型なので
ポインタ型でないとusecase.UserRepositoryのメソッド群との互換が取れません。

結果として&database.UserRepository{}だけがusecase.UserRepositoryになれるわけです。

投稿2019/04/07 22:28

nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2019/04/08 00:58

呼び出しをどちらでも融通効くようにしてくれているのは呼び出し側のコンパイルの成果です。 `database.UserRepository`と`*database.UserRepository`はあくまで別の型です。 どちらとinterfaceと互換があるのかをチェックするのかがコンパイル時にチェックされます。 特に実行時にtype-assertionを記述した場合は実行時にチェックすることになるわけですが、 どちらとも適合するかどうかをチェックすることは冗長でCPUを浪費してしまいます。 なので指定された型だけをinterfaceにマッチするかどうかしかチェックしません。 また、メソッドレシーバが値型とポインタ型が混在しつつinterface型への代入を試みようとすると エラーになります。
yaeyama

2019/04/08 03:13

コンパイラが変換するのにコーディング時は気にしないといけないのですか?ならコンパイラが暗黙的に変換する意味がないように思えるのですが、、、 ポインタ型でしか受け付けないのなら ``` type UserInteractor struct { UserRepository *UserRepository } ``` としないのはなぜですか?
nobonobo

2019/04/08 05:39 編集

``` type UserInteractor struct { UserRepository UserRepository } ``` ここに書かれたUserRepositoryフィールドのUserRepositoryインターフェース型とdatabase.UserRepository構造体型は全く異なるものなんですが区別はついていますか? (なぜパッケージ名を省略せずに書いているのかに注目してください) 「usecase.UserRepository」を「*usecase.UserRepository」という表現をした場合、「usecase.UserRepositoryインターフェースへのポインタ型」という表現になり、 一般的に使われない記述になります。
nobonobo

2019/04/08 05:45

また、メソッドレシーバの型を値型にするのかポインタ型にするのかは戦略的に実装者が決めるところです。その指定を省略してコンパイラに実装者の意図と実際の挙動のつじつま合わせはできません。呼び出す側は相手が値型かポインタ型かコンパイラが把握しているので都合の良い方法で呼び出しするようにコンパイルすることができます。
nobonobo

2019/04/08 05:49 編集

database.UserRepositoryのメソッド群のレシーバを値型にすることでUserRepositoryインターフェース型にdatabase.UserRepositoryの値型を代入することはできます。(この事例だと元のコードが期待した挙動にはなりませんがエラーは出なくなります) なので「ポインタ型でしか受け付けない」というわけでもありません。
yaeyama

2019/04/08 08:42 編集

途中で送信してしまいました。 噛み合いませんね。 > database.UserRepository構造体型は全く異なるものなんですが区別はついていますか? 違うことはわかりますが、だから質問をしているわけなんですが。 ``` type UserInteractor struct { UserRepository UserRepository } ``` は `usecase/user_repository.go` のUserRepository interfaceであり Store FindById FindAll を満たすものである必要がある認識でよろしいですか?
yaeyama

2019/04/08 08:41

質問の本質は値型の変数にポインタ型の変数を代入しているのがなぜなのかということです。
nobonobo

2019/04/08 11:15 編集

一つ前のコメントの認識で合っています。 interface型は定義されたメソッドリスト情報に紐づいた「型と値の2要素を内部にもつ構造を抽象化したもの」です。「ポインタ型とポインタ」を格納することができますし、「構造体型と構造体の値」をも格納することができます。 interface型変数には値もポインタもどちらのタイプも格納できるので わざわざ構造体型ポインタを格納するために「interface型へのポインタ」というものを利用することはありません。つまり、「interface型」のポインタという概念はGoでは通常使いません。 そしてUserRepositoryにdatabase.UserRepository{}が入らず&database.UserRepository{}しか入らないことは「エラーメッセージが素直にそのことを理解する助けになる」はずです。
nobonobo

2019/04/09 00:32

あとは、Goでは型を大別した時、値型とポインタ型とインターフェース型の三種類あると解釈すると良いかと。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問