構造体とimpl — Rubyのclassが2つに分かれる
structでデータ、implでメソッド、traitで多態性
Rubyではクラスがデータ(インスタンス変数)とメソッドを一体にまとめる。Rustはこれを分離した。
# Ruby — データとメソッドが一体
class User
attr_reader :name, :email
def initialize(name, email)
@name = name
@email = email
end
def display
"#{name} <#{email}>"
end
end
// Rust — データ
struct User {
name: String,
email: String,
}
// Rust — メソッド(別ブロック)
impl User {
fn new(name: String, email: String) -> Self {
User { name, email }
}
fn display(&self) -> String {
format!("{} <{}>", self.name, self.email)
}
}
&self — Rubyの暗黙的selfが明示的に
Rubyではインスタンスメソッド内でselfは自動的に使える。nameだけでself.nameになる。Rustでは第1引数として&selfを明示する必要がある。
&self— 読み取り専用(Rubyの通常メソッド)&mut self— 値を変更可能(Rubyの!メソッドに近い)self— 所有権を取る(呼び出し後に元のデータは使用不可)
impl User {
fn name(&self) -> &str { &self.name } // 読み取り
fn set_name(&mut self, name: String) { self.name = name; } // 書き込み
fn into_name(self) -> String { self.name } // 所有権移動
}
関連関数 — Rubyのクラスメソッド
&selfを受け取らない関数はRubyのクラスメソッドに相当する。
# Ruby
User.create(name: "sehwa") # クラスメソッド
// Rust
User::new(String::from("sehwa"), String::from("s@e.com")); // ::で呼び出し
.ではなく::で呼ぶ。newはキーワードではなくただの慣例。
trait — Rubyのmodule
Rubyでincludeでモジュールを混ぜるように、Rustではtraitをimplする。
# Ruby
module Printable
def print_info
puts to_s
end
end
class User
include Printable
end
// Rust
trait Printable {
fn print_info(&self);
}
impl Printable for User {
fn print_info(&self) {
println!("{}", self.display());
}
}
核心的な違い:Rubyのmoduleは実装を含むことができ多重includeも自由。Rustのtraitはインターフェースに近く、実装はimpl Trait for Typeで行う。ダイアモンド継承問題がない。
derive — 自動実装
RubyでComparableをincludeして<=>を定義すると残りの比較演算子が自動で生まれる。Rustのderiveは似たようなもの。
#[derive(Debug, Clone, PartialEq)]
struct User {
name: String,
email: String,
}
Debugはputs user.inspectのようなデバッグ出力、Cloneは.dup、PartialEqは==比較。アノテーション1行で自動実装される。
継承がない
Rustに継承はない。これがRuby開発者にとって最大のパラダイム転換。コード再利用はコンポジション(構造体の中に別の構造体を入れる)かtraitで解決する。
キーポイント
structでデータ定義、implでメソッド定義 — 別ブロック
&self = 読み取り、&mut self = 書き込み、self = 所有権移動
traitはRubyのmodule includeに対応 — 継承の代わりに使用
deriveでDebug、Clone、PartialEq等を自動実装
メリット
- ✓ データと振る舞いが分離されて構造が明確
- ✓ traitにダイアモンド継承問題がない
デメリット
- ✗ Rubyのオープンクラス(既存クラスにメソッド追加)ができない
- ✗ 継承なしでのコード再利用という考え方の転換が必要