概要
- 異なる意味 を持つ値に 型を分ける 重要性を解説
- intやstring など汎用型の乱用による バグ例 を紹介
- Go言語 と libwxライブラリ での具体例
- 型ごとの 変換メソッド や 型安全性 の利点を説明
- GitHub でサンプルコードを公開
単純な値に独自型を定義するテクニック
- 多くのプログラムで、 intやstring、UUID などのシンプルな型をそのまま利用するケースが多い
- しかし、これらの型を 異なる意味 で使い回すと、 IDの取り違え や 引数の順序ミス などのバグが発生しやすい
- 例えば、 UserID と AccountID がどちらもstringやUUID型の場合、 意図しない代入や関数呼び出し がコンパイル時に検出できない
- 解決策として、 用途ごとに新しい型 を定義し、 意味づけ を明確にすることが有効
- 型を分けることで、 コンパイラによる型チェック が働き、 人為的ミス を未然に防止
Go言語での例
-
例えば、Goでは次のようにIDごとに型を定義可能
type AccountID uuid.UUID type UserID uuid.UUID -
このように型を分けると、 UserIDを受け取る関数にAccountIDを渡すとコンパイルエラー となる
-
逆に、 型を分けずにuuid.UUID型のまま使うと、引数の取り違えがコンパイル時に検出できず、ランタイムでバグ発生
libwxライブラリでの型安全性
- Go用の libwxライブラリ では、 気象や大気の計算 に使う値ごとに型を定義
- 例: 温度はTempF型やTempC型、湿度はRelHumidity型 など
- 型ごとに 変換メソッド(例:Km.Miles()) も実装
- これにより、 float64だけで値を扱う場合に起きやすい単位や意味の取り違え を防止
- 関数の引数に 間違った型 を渡すと、 コンパイルエラー で即座に検出
コード例
```go
temp := libwx.TempF(84) // 華氏温度
humidity := libwx.RelHumidity(67) // 相対湿度(%)
// 誤って摂氏温度が必要な関数に華氏温度を渡すとエラー
fmt.Printf("Dew point: %.1fºF\n", libwx.DewPointC(temp, humidity))
// => エラー: 'temp' (libwx.TempF型)をTempC型として使えない
// 引数の順序を間違えてもエラー
fmt.Printf("Dew point: %.1fºF\n", libwx.DewPointF(humidity, temp))
// => エラー: 引数の型が一致しない
```
型システムの活用のすすめ
- 型システムはバグ防止の強力な味方
- モデルごとにID型を分ける、関数の引数に floatやintだけを使わない 設計が推奨
- intやstring、UUID を使い回すことで、 実際のシステムでも多くのバグが発生
- Goのような 比較的シンプルな型システム でも、 型定義による安全性向上 は十分に実現可能
- このテクニックは簡単なのに広く使われていない ため、ぜひ積極的に導入を推奨
サンプルコードとリソース
- 本記事で紹介した コード例や詳細 はGitHubで公開
- cdzombak/libwx_types_lab リポジトリにて参照・貢献が可能
- URL: https://github.com/cdzombak/libwx_types_lab