• 使える型の範囲を限定する型制約としては、anycomparable以外にも任意のインタフェースが指定できる
  • 例えば、空間に置かれる点の距離などに関する演算の場合
    • まず、型パラメータを使って「同じ型を持つ2つの値を保持する」・「fmt.Stringerを実装する」を満たす構造体を定義する
type Pair[T fmt.Stringer] struct { // Tはfmt.Stringerを実装する
	// Tはfmt.StringerインタフェースのString()メソッドをもつ構造体
	Val1 T
	Val2 T
}
  • 次に型パラメータを使って「fmt.Stringerを埋め込んでいる」・「別の同じ型の値と比較し、float64を返すDiffメソッドを持つ」のインタフェースを定義する
type Differ[T any] interface {
	fmt.Stringer // fmt.Stringerを埋め込むことでPairの制約を伝搬させる
	Diff(T) float64
}
  • 上記2つの型を使用しペア間の距離が近いPairを返すFindCloser関数を定義する
// インタフェースDifferを満たす型Tを型パラメータとして指定
func FindCloser[T Differ[T]](pair1, pair2 Pair[T]) Pair[T] {
	d1 := pair1.Val1.Diff(pair1.Val2) // pair1の距離
	d2 := pair2.Val1.Diff(pair2.Val2) // pair2の距離
	if d1 < d2 {                      // pair1のほうがpair2よりも距離が近い
		return pair1
	}
	return pair2 // pair2のほうがpair1よりも近いか同じ距離
}
  • 実際に使用する(2D)
// 型Point2Dの定義
type Point2D struct { // 2次元の点
	X, Y int // X座標、Y座標
}
 
// Point2Dに付随するメソッドの定義
// 1. メソッドString -- 自分の(2次元の)「座標」を出力
func (p2 Point2D) String() string {
	return fmt.Sprintf("[%d,%d]", p2.X, p2.Y)
}
 
// 2. メソッドDiff -- 引数で渡された2次元の点と自分との距離を計算
func (p2 Point2D) Diff(from Point2D) float64 {
	x := p2.X - from.X
	y := p2.Y - from.Y
	return math.Sqrt(float64(x*x) + float64(y*y)) // 2次元距離
}
 
func main() {
	fmt.Println("=== 2Dバージョン ===")
	pair2Da := Pair[Point2D]{Point2D{1, 1}, Point2D{5, 5}}
	fmt.Println("1つ目のペア:", pair2Da) // {[1,1] [5,5]}
	pair2Db := Pair[Point2D]{Point2D{10, 10}, Point2D{15, 5}}
	fmt.Println("2つ目のペア:", pair2Db) // {[10,10] [15,5]}
	closer := FindCloser(pair2Da, pair2Db)
	fmt.Println("距離が近いペア:", closer) // {[1,1] [5,5]}
}
  • 実際に使用する(3D)
// 型Point3Dの定義
type Point3D struct { // 3次元の点
	X, Y, Z int // X座標、Y座標、Z座標
}
 
// Point3Dに付随するメソッドの定義
// 1. メソッドString -- 自分の(3次元の)「座標」を出力
func (p3 Point3D) String() string {
	return fmt.Sprintf("[%d,%d,%d]", p3.X, p3.Y, p3.Z)
}
 
// 2. メソッドDiff -- 引数で渡された3次元の点と自分との距離を計算
func (p3 Point3D) Diff(from Point3D) float64 {
	x := p3.X - from.X
	y := p3.Y - from.Y
	z := p3.Z - from.Z
	return math.Sqrt(float64(x*x) + float64(y*y) + float64(z*z)) // 3次元距離
}
 
func main() {
	fmt.Println("=== 3Dバージョン ===")
	pair3Da := Pair[Point3D]{Point3D{1, 1, 10}, Point3D{5, 5, 0}}
	fmt.Println("1つ目のペア:", pair3Da) // {[1,1,10] [5,5,0]}
	pair3Db := Pair[Point3D]{Point3D{10, 10, 10}, Point3D{11, 5, 0}}
	fmt.Println("2つ目のペア:", pair3Db) // {[10,10,10] [11,5,0]}
	closer2 := FindCloser(pair3Da, pair3Db)
	fmt.Println("距離が近いペア:", closer2) // {[10,10,10] [11,5,0]}
}
  • 実際に使用する(コンパイルエラー)
type StringerString string // メソッドStringをもつstring
 
// StringerStringに付随するメソッドの定義
// インタフェースDifferは満たされていない
// 1.  メソッドString
func (ss StringerString) String() string {
	return string(ss)
}
 
func main() {
	// 同じ型のペアが指定されていない場合、コンパイル時エラー
	pair2Da := Pair[Point2D]{Point2D{1, 1}, Point2D{105, 5}}
	pair3Da := Pair[Point3D]{Point3D{1, 1, 10}, Point3D{5, 5, 0}}
	closer3 := FindCloser(pair2Da, pair3Da)
	// ./prog.go:12:33: in call to FindCloser, type Pair[Point3D] of pair3Da does not match inferred type Pair[Point2D] for Pair[T]
 
	// Differを実装していない場合、コンパイル時エラー
	closer4 := FindCloser(Pair[StringerString]{"a", "b"},
	// ./prog.go:9:23: in call to FindCloser, T (type StringerString) does not satisfy Differ[T] (missing method Diff)
}