代入可能性の判定において「それが定義型かどうか」が重要視される理由は、Go言語が「型安全性の厳格な保護」と「記述の柔軟性(利便性)」のバランスを上手くとるように設計されているためです。
大きく分けて以下の2つの理由があります。
1. 意味を持たせた「定義型」同士の意図しない混同を防ぐため(型安全性の保護)
Goにおいて、typeキーワードを使って作成した新しい型や、組み込みのintやstringなどは「定義型(defined type)」と呼ばれます。言語仕様上、定義型は「生成元となる型を含む、他のあらゆる型とは異なるもの」として厳密に区別されます。
ユーザーがわざわざ type MyInt int のように新しい定義型を作る主な理由は、その型専用のメソッドを関連付けたり、データの意味合いを明確にしてバグを防いだりするためです。 もし、基底型が同じだからという理由だけで int と MyInt(どちらも定義型)の暗黙的な直接代入を許してしまうと、せっかく型を分けた意味がなくなり、意図しない値の混入を防げなくなってしまいます。
これを防ぐため、両者が定義型である場合は暗黙的な代入を禁止し、明示的な型変換(キャスト)を強制しています。同様に、移植性の問題を避けるために int や float64 などの組み込みの数値型もすべて定義型とされており、これらを混在させる場合も明示的な変換が要求されます。
2. 「型リテラル」を扱う際のコードの冗長さを防ぐため(利便性の確保)
一方で、[]int(スライス)や map[string]int(マップ)のように、構造をそのまま記述したものは「型リテラル」と呼ばれ、これらは定義型ではありません。
もしGoがすべてのケースにおいて「両辺が完全に同じ型でなければ代入できない」または「両辺の基底型が同じでも明示的なキャストが毎回必要」という極端なルールを採用していたらどうなるでしょうか。その場合、type MyIntSlice []int という型を定義したとき、そこに []int のデータを代入するたびに、毎回明示的なキャストが必要になり、コードが非常に冗長になってしまいます。
これを避けるため、「少なくとも一方が定義型ではない(=単なるデータ構造の表現である型リテラルなど)」ケースに限っては、基底型(根本的なデータ構造)が同じであればそのまま代入を許可するというルールが設けられています。これにより、[]int のデータを MyIntSlice 型の変数に直接代入できる柔軟性が保たれています。
まとめ
要するに、Go言語は「意味を持たせて独立させた型(定義型)」同士は厳格に区別して安全性を担保しつつ、「単なるデータ構造の記述(非定義型の型リテラル)」とのやり取りは許可して実用的な利便性を確保しているのです。だからこそ、代入できるかどうかを判定する際に「定義型であるかどうか」が非常に重要な基準となります。