Rustのトレイト
Rustのトレイトは、型が持つべき振る舞い(メソッド)を定義する機能です。これは、他の言語におけるインターフェースや型クラスに非常に似ています。
例として、Summary
というトレイトを定義して見てみましょう。
trait Summary {
fn summarize(&self) -> String;
}
このトレイトはsummarize
というメソッドを持つ型を定義します。そして、このトレイトを実装することで、様々な型が同じように振る舞うことができます。
struct NewArticle {
headline: String,
content: String,
}
impl Summary for NewArticle {
fn summarize(&self) -> String {
format!("{} - {}", self.headline, self.content)
}
}
トレイトは、ジェネリクスと組み合わせて使うことで、特定のトレイトを実装する任意の型に対して汎用的な関数を定義できます。
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
OCamlのシグネチャ
一方、OCamlのシグネチャは、モジュールのインターフェースを定義するものです。Rustのトレイトが「型」に焦点を当てるのに対し、OCamlのシグネチャは「モジュール」に焦点あてます。
module type OrderedType = sig
type t
val compare : t -> t -> int
end
このシグネチャは、t
という型と、その型を比較するcompare
関数を持つモジュールを定義します。このシグネチャを満たすモジュールを作成することで、同じインターフェースを持つ異なる実装を用意できます。
module IntOrd = struct
type t = int
let compare = Int.compare
end
OCamlでは、このシグネチャを引数に取る「ファンクタ」を使うことで、Rustのトレイトとジェネリクスが実現する汎用的なコードを記述します。
module Sortable (Ord: OrderedType) = struct
let sort_list lst =
List.sort (Ord.compare) lst
end
比較: 思想とアプローチの違い
特徴 | Rustのトレイト | OCamlのシグネチャ |
---|---|---|
対象 | 型(type) | モジュール(module) |
目的 | 型の振る舞うを定義し、 多相性を実現 | モジュールのインターフェース を定義し、抽象化を実現 |
多相性の実現 | ジェネリクスと組み合わせる | ファンクタと組み合わせる |
Rustのトレイトは、C++のテンプレートやHaskellの型クラスに近い、型レベルでの抽象化を強く意識しています。これは、特定の型が満たすべき振る舞いを直接的に表現するのに優れています。
一方、OCamlのシグネチャは、よりモジュール単位での抽象化に焦点を当てています。ファンクタというもうジュール操作の仕組みと組み合わせることで、モジュールという大きな単位でコードを再利用する設計が可能です。
まとめ
どちらも強力な抽象化のツールですが、その思想は異なります。
Rustのトレイトは、型が特定の振る舞いを持つことを保証し、アドホック多相性(同じ関数名で異なる引数の型を受け取り、それぞれの型に対して異なる処理を行う多相性のこと)による柔軟なコードを可能にします。
OCamlのシグネチャは、モジュールが特定のインターフェースを持つことを保証し、ファンクタを通じてモジュールレベルでの再利用性を高めます。
どちらの言語も、それぞれの設計思想に基づいて、堅牢で再利用性の高いコードを書くための優れた仕組みを提供しています。両者を理解し比較することで、それぞれの言語への理解が深めることができるといいなと思います。