はじめに
"ことわざをプログラミング"することで Rust を勉強していきます.
本日のお題: "ペンは剣よりも強し"
イギリスの政治家・小説家ブルワー・リットンの戯曲『リシュリュー』にある「The pen is mightier than the sword.」の訳。
文章で表現される思想は世論を動かし、武力以上に強い力を発揮するということ。
http://kotowaza-allguide.com/he/penwakenyoritsuyoshi.html
「ペンが剣より強い」とはどういうことか,プログラミングで表現していきます.
本日の学び
-
traitによる抽象的な振る舞いの実装
GitHubレポジトリ
仕様
- ペンは書くことができ,剣は振るうことができる.
- ペンも剣も,武器として扱うことができる.
- 武器は「それ自体の殺傷能力(killing power)」と,それを振るうことで「世の中を動かす力(influence)」の二種類のチカラを持っている.
- 武器はお互いにチカラを比べることができる.
- 剣はペンよりも殺傷能力が優れるが,ペンは剣よりも世の中を動かす影響力が強い.
コード全体
// Proverb-Programming: "The pen is mighter than the sword"
// Weapon trait
trait Weapon {
fn get_killing_power(&self) -> u32;
fn get_influence(&self) -> u32;
// Weapon can fight with another weapon.
fn can_beaten<T: Weapon>(&self, another:& T) -> bool{
self.get_killing_power() > another.get_killing_power()
}
// Weapon can compare its influence power with another weapon.
fn mighter_than<T: Weapon>(&self, another:& T) -> bool{
self.get_influence() > another.get_influence()
}
}
// Pen class
pub struct Pen {
}
impl Pen {
fn new() -> Pen {
Pen{}
}
fn write(&self, something: &str){
println!("{}",something);
}
}
impl Weapon for Pen {
fn get_killing_power(&self) -> u32{
0
}
fn get_influence(&self) -> u32{
1000
}
}
// Sword class
pub struct Sword {
}
impl Sword {
fn new() -> Sword {
Sword{}
}
fn slash(&self){
println!("Zip!");
}
}
impl Weapon for Sword {
fn get_killing_power(&self) -> u32{
100
}
fn get_influence(&self) -> u32{
50
}
}
fn main() {
let the_pen: Pen = Pen::new();
let the_sword: Sword = Sword::new();
the_pen.write("This is a pen.");
the_sword.slash();
if the_sword.can_beaten(&the_pen) {
println!("The sword can beaten the pen.");
}
if the_pen.mighter_than(&the_sword) {
println!("The pen is mighter than the sword.");
}
}
実行結果
$./pen_is_mighter_than_sword
This is a pen.
Zip!
The sword can beaten the pen.
The pen is mighter than the sword.
コードの解説
ペンクラス(Pen)と剣クラス(Sword)は,両方とも武器(Weapon)であるという側面を持ちます(ホントか?).
できるならWeaponクラスを作り,PenやSwordはそこから継承したいところですが,Rustではそれができないようです.
Rust では継承の代わりに,抽象的な振る舞いを持つ Weapon トレイトを定義して,PenやSwordにWeaponトレイトを実装させることで,複数のクラスに共通の振る舞いを持たせることができます.
trait Weapon {
fn get_killing_power(&self) -> u32;
fn get_influence(&self) -> u32;
// Weapon can fight with another weapon.
fn can_beaten<T: Weapon>(&self, another:& T) -> bool{
self.get_killing_power() > another.get_killing_power()
}
// Weapon can compare its influence power with another weapon.
fn mighter_than<T: Weapon>(&self, another:& T) -> bool{
self.get_influence() > another.get_influence()
}
}
impl Weapon for Pen{...}
impl Weapon for Sword{...}
impl Pen {
fn write(&self, something: &str){
println!("{}",something);
}
}
impl Sword {
fn slash(&self){
println!("Zip!");
}
}
PenもSwordも,Weaponトレイトの持つ次のメソッドを共通して使うことができます:
- get_killing_power: 殺傷能力を返す
- get_influence: 影響力を返す
- can_beaten: 相手の武器を打ち砕くことができるかを返す
- mighter_than: 相手の武器より「強い」かを返す
ただし,それぞれのメソッドの具体的な振る舞いは,共通していることもあれば,全く異なることもあります. can_beaten と mighter_than の両メソッドは,どのクラスでも絶対に同じ処理になるので,いっそのこと trait に直接定義してしまっています.
また,Penクラスが使えるwriteメソッドをSwordクラスは使うことができず,かわりにSwordクラスが使えるslashメソッドをPenクラスは使うことができません.
最後に
「この書き方は普通じゃない」「こんなエレガントな書き方があるよ」というアドバイスがありましたらぜひコメントや編集リクエストをお願いします.