第19回:型システムの応用
~状態遷移の型安全な表現と型レベルプログラミング~
はじめに
Rustの型システムを活用して、コンパイル時に安全性を保証する高度なプログラミングパターンについて解説します。
状態遷移の型安全な表現
// 状態を表す型レベルマーカー
pub trait State {}
// 初期状態
pub struct Initial;
impl State for Initial {}
// 検証済み状態
pub struct Validated;
impl State for Validated {}
// 処理済み状態
pub struct Processed;
impl State for Processed {}
// 完了状態
pub struct Completed;
impl State for Completed {}
// 状態遷移を持つデータ構造
pub struct StatefulData<S: State> {
data: Vec<u8>,
metadata: HashMap<String, String>,
_state: PhantomData<S>,
}
impl StatefulData<Initial> {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
metadata: HashMap::new(),
_state: PhantomData,
}
}
pub fn validate(self) -> Result<StatefulData<Validated>, ValidationError> {
// バリデーションロジック
if self.data.is_empty() {
return Err(ValidationError::EmptyData);
}
Ok(StatefulData {
data: self.data,
metadata: self.metadata,
_state: PhantomData,
})
}
}
impl StatefulData<Validated> {
pub fn process(mut self) -> Result<StatefulData<Processed>, ProcessError> {
// 処理ロジック
self.metadata.insert("processed_at".to_string(),
SystemTime::now().to_string());
Ok(StatefulData {
data: self.data,
metadata: self.metadata,
_state: PhantomData,
})
}
}
依存型のエミュレーション
// サイズ制約を持つベクター
pub struct SizedVector<T, const N: usize> {
data: Vec<T>,
_size: PhantomData<[T; N]>,
}
impl<T, const N: usize> SizedVector<T, N> {
pub fn new() -> Self {
Self {
data: Vec::with_capacity(N),
_size: PhantomData,
}
}
pub fn push(&mut self, value: T) -> Result<(), CapacityError> {
if self.data.len() >= N {
return Err(CapacityError::Overflow);
}
self.data.push(value);
Ok(())
}
}
// 範囲制約を持つ数値型
pub struct Bounded<T, const MIN: i64, const MAX: i64> {
value: T,
}
impl<T: Into<i64> + From<i64>, const MIN: i64, const MAX: i64> Bounded<T, MIN, MAX> {
pub fn new(value: T) -> Result<Self, BoundError> {
let numeric_value: i64 = value.into();
if numeric_value < MIN || numeric_value > MAX {
return Err(BoundError::OutOfRange);
}
Ok(Self { value })
}
pub fn get(&self) -> T {
self.value
}
}
型レベルプログラミング
// 型レベル数値
pub trait TypeNum {
const VALUE: usize;
}
// 型レベルゼロ
pub struct Zero;
impl TypeNum for Zero {
const VALUE: usize = 0;
}
// 型レベル後続
pub struct Succ<N: TypeNum>;
impl<N: TypeNum> TypeNum for Succ<N> {
const VALUE: usize = N::VALUE + 1;
}
// 型レベルリスト
pub struct TypeNil;
pub struct TypeCons<H, T>;
pub trait TypeList {
type Length: TypeNum;
}
impl TypeList for TypeNil {
type Length = Zero;
}
impl<H, T: TypeList> TypeList for TypeCons<H, T> {
type Length = Succ<T::Length>;
}
// 型レベル演算
pub trait Add<Rhs> {
type Output;
}
impl<Rhs: TypeNum> Add<Rhs> for Zero {
type Output = Rhs;
}
impl<N: TypeNum, Rhs: TypeNum> Add<Rhs> for Succ<N>
where
N: Add<Rhs>,
{
type Output = Succ<N::Add<Rhs>>;
}
実装例:型安全なステートマシンの実装
// ステートマシンの実装例
pub trait Transition<From, To> {
fn execute(from: Machine<From>) -> Result<Machine<To>, TransitionError>;
}
pub struct Machine<S: State> {
state: PhantomData<S>,
context: StateContext,
}
// 状態遷移の定義
pub struct StartTransition;
impl Transition<Initial, Running> for StartTransition {
fn execute(from: Machine<Initial>) -> Result<Machine<Running>, TransitionError> {
// 起動処理
Ok(Machine {
state: PhantomData,
context: from.context.start()?,
})
}
}
pub struct PauseTransition;
impl Transition<Running, Paused> for PauseTransition {
fn execute(from: Machine<Running>) -> Result<Machine<Paused>, TransitionError> {
// 一時停止処理
Ok(Machine {
state: PhantomData,
context: from.context.pause()?,
})
}
}
// 使用例
async fn run_state_machine() -> Result<(), MachineError> {
let initial = Machine::<Initial>::new();
// 型安全な状態遷移
let running = StartTransition::execute(initial)?;
let paused = PauseTransition::execute(running)?;
// コンパイル時エラー:
// let error = PauseTransition::execute(paused); // すでにPaused状態なのでコンパイルエラー
Ok(())
}
// 高度なステートマシンの実装
pub struct AdvancedStateMachine<S: State> {
state: PhantomData<S>,
data: StatefulData<S>,
transitions: TransitionRegistry,
}
impl<S: State> AdvancedStateMachine<S> {
pub fn transition<T, To>(self) -> Result<AdvancedStateMachine<To>, TransitionError>
where
T: Transition<S, To>,
To: State,
{
// 遷移の実行
let next = T::execute(self.into_machine())?;
Ok(AdvancedStateMachine {
state: PhantomData,
data: next.data,
transitions: self.transitions,
})
}
pub fn register_observer<O: StateObserver<S>>(&mut self, observer: O) {
self.transitions.register_observer(observer);
}
}
今回のまとめ
- 型を利用した状態遷移の安全性保証
- 依存型のエミュレーション手法
- 型レベルプログラミングの実践
- 型安全なステートマシンの実装
次回予告
第20回では、WITとシリアライゼーションについて解説します。Component Model統合とクロスプラットフォームデータ交換の実装について詳しく見ていきます。
参考資料
- Type-Level Programming in Rust
- State Machine Design Patterns
- Advanced Type System Features
- Type-Safe API Design