読者です 読者をやめる 読者になる 読者になる

なんかActorができたので

Advent Calendar 2016 作った Rust

qiita.com 23日目


Rustのコードを書いて遊んでいたら、「あっこれActorじゃん」となって、いつの間にかActorができていたのでそれについて書きます。

[2016/12/24追記] 指摘ありましたが、これをActorと呼ぶのはまずかったです。

今回作ったものは以下のcruxというリポジトリです。 github.com

Actorの作りかた

Actorトレイトを実装する事によって作ります。

enum PrintAction {
    Set(i32),
    Print,
}

#[derive(Clone)]
struct PrintActor {
    value: i32
}
impl Actor for PrintActor {
    type Action = PrintAction;
    fn receive(&mut self, action: Self::Action) {
        match action {
            PrintAction::Set(value) => self.value = value,
            PrintAction::Print => println!("{}", self.value),
        }
    }
}

このPrintActorではPrintAction::Printを受け取った時に、最後に受け取ったPrintAction::Set(value)valueを表示します。

Actorの起動はspawn_actorで行います。

let mut print_actor = spawn_actor(PrintActor { value: 0 });
print_actor.dispatch_sync(PrintAction::Print); 
// => 0
print_actor.dispatch_sync(PrintAction::Set(3);
print_actor.dispatch_sync(PrintAction::Set(5);
print_actor.dispatch_sync(PrintAction::Print); 
// => 5

構造体の中に別のアクターを含める事でActor同士での通信が行えます。

#[derive(Clone)]
struct SumActor {
    sum: i32,
    next: ActorStore<PrintActor>,
}
impl Actor for SumActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.sum += action;
        self.next.dispatch_sync(PrintAction::Set(self.sum));
    }
}

SumActorは受け取った値を次々に足していって、最新の値をPrintActionに送り続けます。

さらに、1を足して別のActorに送るIncrementActorと二乗するSquareActorを作ります。

#[derive(Clone)]
struct IncrementActor {
    next: ActorStore<SquareActor>
}
impl Actor for IncrementActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.next.dispatch_sync(action + 1);
    }
}

#[derive(Clone)]
struct SquareActor {
    next: ActorStore<SumActor>
}
impl Actor for SquareActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.next.dispatch_sync(action * action);
    }
}

最後にこれまでに作ったActorを使って0(0+1)^2 + (1+1)^2 + (2+1)^2 + (3+1)^2を表示するプログラムを作ると以下のようになります。

let mut print_actor = spawn_actor(PrintActor { value: 0 });
let sum_actor = spawn_actor(SumActor {
    sum: 0, next: print_actor.clone()
});
let square_actor = spawn_actor(SquareActor {
    next: sum_actor.clone()
});
let mut increment_actor = spawn_actor(IncrementActor {
    next: square_actor.clone()
});

print_actor.dispatch_sync(PrintAction::Print);
// => 0
increment_actor.dispatch_sync(0);
increment_actor.dispatch_sync(1);
increment_actor.dispatch_sync(2);
increment_actor.dispatch_sync(3);
print_actor.dispatch_sync(PrintAction::Print);
// => 30

全体のソースコードcrux-examples/main.rs at master · ryo33/crux-examples · GitHubにあります。

このような感じでActorっぽいものが簡単に作れて楽しいです。

ありがとうございました。