cannot use … (type …) as type … in argument/return/assignment とは
Go言語で開発していると、cannot use ... (type ...) as type ... というエラーに頻繁に遭遇することがあります。これは、ある型を別の型の変数に代入しようとしたり、関数の引数に渡そうとしたりした際に、型が一致しないために発生するコンパイルエラーです。Go言語の厳格な型システムによって守られている証拠でもありますが、慣れないうちは戸惑うかもしれません。

Go言語の型エラー、最初は「なんでこんなに厳しいんだ!」って思うかもしれませんが、これこそがGoの堅牢さの源なんですよね。コンパイル時にエラーが見つかるのは、ある意味ラッキーです!
実行環境ごとのエラーメッセージ
| 環境 | エラーメッセージ |
|---|---|
| Go Compiler (CLI) | main.go:7:18: cannot use i (type int) as type float64 in assignment |
| VS Code (go-langserver) | cannot use i (type int) as type float64 in assignment |
| Go Playground | prog.go:7:18: cannot use i (type int) as type float64 in assignment |
エラーの発生パターン
このエラーは主に以下のようなケースで発生します。
パターン1: 異なる数値型間の代入
package main
import "fmt"
func main() {
var i int = 10
var f float64 = i // int型をfloat64型に直接代入しようとしている
fmt.Println(f)
}
Go言語では、たとえ数値型同士であっても int 型と float64 型は異なる型として扱われます。他の言語のように暗黙的な型変換は行われないため、明示的に型変換を行う必要があります。
package main
import "fmt"
func main() {
var i int = 10
var f float64 = float64(i) // int型をfloat64型に明示的に変換
fmt.Println(f)
}
パターン2: 構造体がインターフェースのメソッドを完全に実装していない
package main
import "fmt"
type Speaker interface {
Speak() string
}
type Dog struct {}
func (d Dog) Bark() string { // Speak() ではなく Bark() を実装
return "Woof"
}
func main() {
var s Speaker = Dog{} // Dog型はSpeakerインターフェースを実装していない
fmt.Println(s.Speak())
}
Go言語のインターフェースは、そのインターフェースが定義するすべてのメソッドを型が実装している場合にのみ、そのインターフェース型として扱われます。Dog 型は Bark() メソッドを実装していますが、Speaker インターフェースが要求する Speak() メソッドを実装していないため、型不一致エラーが発生します。
package main
import "fmt"
type Speaker interface {
Speak() string
}
type Dog struct {}
func (d Dog) Speak() string { // SpeakerインターフェースのSpeak()を実装
return "Woof"
}
func main() {
var s Speaker = Dog{} // Dog型はSpeakerインターフェースを実装している
fmt.Println(s.Speak())
}
パターン3: ポインタ型と値型の混同
package main
import "fmt"
type Person struct {
Name string
}
func printName(p Person) { // 値型 Person を引数に取る
fmt.Println(p.Name)
}
func main() {
var p *Person // ポインタ型 *Person
printName(p) // ポインタ型を値型を期待する関数に渡そうとしている
}
Go言語では、ポインタ型 (*Person) と値型 (Person) は厳密に区別されます。printName 関数は Person 型の値を引数に期待していますが、main 関数で宣言された p は *Person 型のポインタです。ポインタの指す値にアクセスするには、デリファレンス (*p) が必要です。
package main
import "fmt"
type Person struct {
Name string
}
func printName(p Person) {
fmt.Println(p.Name)
}
func main() {
var p *Person = &Person{Name: "Alice"} // Person型のポインタを初期化
printName(*p) // ポインタをデリファレンスして値型を渡す
// または最初から値型で宣言する
var p2 Person = Person{Name: "Bob"}
printName(p2)
}




特に異なる数値型間の変換は、他の言語だと暗黙的にやってくれることが多いので、Goでハマりがちですよね。明示的な float64(i) はGoの基本中の基本です。
よくあるバリエーション
cannot use nil as type ... の場合
Go言語では、nil は特定の型を持たないため、インターフェース型やスライス、マップ、チャネル、関数、ポインタ型には代入できますが、具体的な構造体などの値型には代入できません。このエラーは、nil を値型変数に代入しようとしたり、nil が許容されない場所で使われた場合に発生します。
package main
import "fmt"
type MyStruct struct {
Value int
}
func main() {
// bad_code: 値型にnilを代入しようとしている
// var s MyStruct = nil
// good_code: ポインタ型であればnilを代入可能
var sPtr *MyStruct = nil
fmt.Println(sPtr)
// good_code: 値型を初期化する
var s MyStruct
fmt.Println(s)
}
cannot use type *... as type ... の場合
これは、ポインタ型(*Type)を、値型(Type)を期待する場所で使おうとした場合に発生します。関数引数や構造体のフィールドなどでよく見られます。Goのポインタと値の厳密な区別を理解し、必要に応じてデリファレンス (*) するか、関数の引数型を変更する必要があります。
package main
import "fmt"
type Data struct { Name string }
func processData(d Data) { fmt.Println("Processing", d.Name) }
func main() {
ptr := &Data{Name: "test"}
// bad_code: ポインタ型を値型を期待する関数に渡そうとしている
// processData(ptr)
// good_code: デリファレンスして値型を渡す
processData(*ptr)
// good_code: 関数側がポインタ型を受け取るようにする
processDataPtr(ptr)
}
func processDataPtr(d *Data) { fmt.Println("Processing ptr", d.Name) }
cannot use type string as type []byte の場合
Go言語において string 型と []byte (バイトスライス) 型は異なる型であり、暗黙的な変換は行われません。ファイルI/Oやネットワーク通信、暗号化処理などでバイト列を扱う際に、文字列を直接渡そうとするとこのエラーが発生します。明示的な型変換 []byte(str) が必要です。
package main
import "fmt"
func sendBytes(data []byte) { fmt.Printf("Sent: %s\n", string(data)) }
func main() {
message := "Hello, Go!"
// bad_code: string型を[]byte型を期待する関数に渡そうとしている
// sendBytes(message)
// good_code: string型を[]byte型に明示的に変換
sendBytes([]byte(message))
}
フレームワーク別の発生パターン
Ginフレームワーク (JSON/HTTPリクエストボディのバインディング)での発生パターン
GinフレームワークでHTTPリクエストのJSONボディを構造体にバインドする際、リクエストボディの構造とバインド先のGo構造体のフィールド名や型が一致しないと、内部的に型不一致が発生することがあります。特にJSONタグの付け忘れや、数値型と文字列型の混同が原因です。
// bad_code (JSONタグの付け忘れ)
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type User struct {
Name string
Age int // JSONタグがない
}
func main() {
r := gin.Default()
r.POST("/users", func(c *gin.Context) {
var user User
// リクエストボディ: {"name": "Alice", "age": "30"}
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, user)
})
r.Run(":8080")
}
// good_code (JSONタグを追加)
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"` // 正しいJSONタグ
}
func main() {
r := gin.Default()
r.POST("/users", func(c *gin.Context) {
var user User
// リクエストボディ: {"name": "Alice", "age": 30} または {"name": "Alice", "age": "30"}
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, user)
})
r.Run(":8080")
}
gRPCサービス (Protobufメッセージ型変換)での発生パターン
gRPCサービスでProtobufによって生成されたメッセージ型と、アプリケーション内で定義したカスタム構造体の間でデータをやり取りする際、フィールドの型やポインタの有無が一致しない場合に cannot use エラーが発生します。特にネストされたメッセージや oneof フィールドで注意が必要です。
// bad_code (フィールド型の不一致)
// proto/user.proto
// message User { string name = 1; int32 age = 2; }
// Goコード
package main
import (
"fmt"
pbu "./proto"
)
type AppUser struct {
Name string
Age string // Protobufのint32とGoのstringが不一致
}
func convertToAppUser(pbUser *pbu.User) AppUser {
return AppUser{
Name: pbUser.GetName(),
Age: pbUser.GetAge(), // int32をstringに代入しようとしてエラー
}
}
// good_code (フィールド型を合わせる)
package main
import (
"fmt"
"strconv"
pbu "./proto"
)
type AppUser struct {
Name string
Age int32 // Protobufのint32とGoのint32を合わせる
}
func convertToAppUser(pbUser *pbu.User) AppUser {
return AppUser{
Name: pbUser.GetName(),
Age: pbUser.GetAge(),
}
}
// 必要であれば、明示的な型変換を行う
func convertAgeToString(pbUser *pbu.User) string {
return strconv.Itoa(int(pbUser.GetAge()))
}



fmt.Printf("%T\n", varName) はGoでの型デバッグの強い味方です。思っている型と実際の型が違う、なんてことは本当によくあるので、困ったらこれを使ってみましょう!
根本原因の特定方法
このエラーに遭遇した場合、最も重要なのは コンパイラエラーメッセージを注意深く読む ことです。メッセージは「cannot use X (type Y) as type Z」という形式で、どの型(Y)をどの型(Z)として使おうとしたかが明確に示されています。もし変数の実際の型が不明な場合は、fmt.Printf("%T\n", varName) を使って実行時に型を確認すると、原因特定に役立ちます。
package main
import "fmt"
func main() {
var data interface{} = 123 // interface{}型
// var data interface{} = "hello" // string型の場合
// デバッグのために変数の型を出力
fmt.Printf("Type of data: %T\n", data)
// この後で型アサーションや型変換を行う際にエラーを回避
if i, ok := data.(int); ok {
fmt.Println("Data is int:", i)
} else if s, ok := data.(string); ok {
fmt.Println("Data is string:", s)
} else {
fmt.Println("Data is unknown type")
}
}
Go言語の型アサーションと型スイッチ
Go言語には、インターフェース型の変数が保持している具体的な型を調べるための 「型アサーション」(v.(T)) と「型スイッチ」(switch v := i.(type)) というメカニズムがあります。これは any(旧 interface{})型のような汎用的なインターフェースから、具体的な型を取り出す際に頻繁に利用されます。型アサーションは、変換したい型が明確な場合に使い、失敗するとランタイムパニックを起こす可能性があります(v, ok := i.(T) で安全にチェック可能)。型スイッチは、複数の型候補がある場合に網羅的に処理を記述できるため、より安全で柔軟な型変換を実現します。
防止策とベストプラクティス
Go言語での型不一致エラーを防ぐには、常に変数の型を意識し、型変換が必要な場合は明示的に行う習慣を身につけることが重要です。インターフェースを使用する際は、そのインターフェースが要求するすべてのメソッドが実装されているかを確認しましょう。また、コードレビューやテストコードを充実させることで、潜在的な型エラーを早期に発見し、修正することができます。
package main
import "fmt"
func addInts(a, b int) int { // 引数と戻り値の型を明確にする
return a + b
}
func main() {
var x int = 5
var y int = 7
result := addInts(x, y)
fmt.Println(result)
// 異なる型の計算は明示的に変換
var f float64 = 3.14
var i int = 10
sum := f + float64(i) // intをfloat64に変換
fmt.Println(sum)
}



インターフェースはGo言語の強力な機能ですが、正しく設計・実装しないと型エラーの原因になります。設計段階でしっかり考え抜くことが大切ですね。
Stack Overflowでの質問状況
Stack Overflowでは、Goに関する質問が約75,050件投稿されており、cannot use … (type …) as type … in argument/return/assignmentは最も頻繁に質問されるエラーカテゴリの一つです。




Go言語の型システムを理解することは、Goマスターへの第一歩です。焦らず、エラーメッセージを読み解き、一つずつ解決していきましょう!
よくある質問(FAQ)
-
Q型アサーション (
.(Type)) と型変換 (Type(value)) の違いは何ですか? -
A
型変換 (
Type(value)) は、異なる型間でデータを新しい型の値に変換する際に使います(例:float64(i))。一方、型アサーション (v.(T)) は、インターフェース型の変数が「実際にどの具象型を保持しているか」を調べ、その具象型として値を取り出す際に使います。型アサーションはランタイムチェックであり、失敗するとパニックを起こす可能性があります。
-
Qこのエラーが原因で本番環境がダウンすることはありますか?
-
A
cannot use ...エラーはコンパイルエラーなので、コードがコンパイルを通過しない限り、本番環境で実行されることはありません。つまり、このエラー自体が直接本番環境をダウンさせることはありません。しかし、コンパイルエラーを放置すれば、当然ながらアプリケーションはデプロイ・実行できません。
-
QGoのジェネリクス (
[T any]) を使うと、型不一致エラーは減らせますか? -
A
はい、ジェネリクスはコードの再利用性を高めつつ、型安全性を維持するのに役立ちます。特定の型に依存しない汎用的な関数やデータ構造を記述できるため、異なる型間の不必要な変換や、インターフェースを使った型アサーションの乱用を減らし、結果的に型不一致エラーの発生を抑制できます。
-
QCI/CDパイプラインでこのエラーを早期に発見するには?
-
A
Goのビルドコマンド (
go build) は、このエラーをコンパイル時に検知します。CI/CDパイプラインの早い段階でgo buildを実行するステップを含めることで、開発者がコードをプッシュした直後やマージする前に、コンパイルエラーを発見し、フィードバックすることができます。
-
Qエラーハンドリングで型不一致を吸収する方法はありますか?
-
A
型不一致エラーはコンパイルエラーであるため、
try-catchのようなランタイムエラーハンドリングでは吸収できません。コードを修正して型が一致するようにするか、明示的な型変換を行う必要があります。インターフェースからの型アサーションでエラーが発生しそうな場合は、value, ok := interfaceVar.(TargetType)のようにokパターンを使ってランタイムパニックを避けることができます。



コメント