Rustを0から学んでみた 〜Part.3〜 generics(で、オーバーロード) 編(TDD Boot Camp 課題3「閉区間と開区間」)

「TDDBCの課題をRustで実施し、言語仕様を学んでいこう!」というシリーズの記事です。

今までの記事は以下となっています。


[ひとことで言うとこんな記事]

以下を学ぶ事ができます。

  • オーバーロード機能が無いRustは、ジェネリックを利用したトレイトを準備することで実現する

[目次]

  • 課題3: 閉区間と開区間
  • 学んだこと:関数のオーバーロードがないRustでの実装は、ジェネリクスを利用したトレイトにて実現
  • 今回の実装の Pull Request
  • 次回:TDDBC「課題4:閉区間/開区間への機能追加」 実施予定

課題3: 閉区間と開区間

課題3-1

  • 閉区間と開区間と等しいか(equals)判定

課題3-2

  • 閉区間と開区間と接続しているか(isConnectedTo)判定

学んだこと:関数のオーバーロードがないRustでの実装は、ジェネリクスを利用したトレイトにて実現

課題3と課題1,2を見てみると、 「同じメソッド名で拡張しなはれ」 と言っているのが自明であります。

Scalaなんかでは、同じメソッド名だとしても引数が違うものを定義するだけの簡単な作業ですが、 Rustにはオーバーロード機能がございません

scalaでの例

def hoge(i: Int){
	// 実際の処理
}

def hoge(s: String){
	// 実際の処理
}

Rustで同じようなことを記述すると error[E0201]: duplicate definitions with nameと言われてコンパイルエラー です。

今回は、以下のrustのドキュメントを元に "ジェネリクス"を用いた方法 で実装していきたいと思います。

トレイトにジェネリックな引数があると、 毎回ジェネリックな型引数の具体的な型を変更してある型に対して複数回実装できるということです。

引用: 高度なトレイト - The Rust Programming Language

実装

実際のコードは以下です

※後日パターン不備で、bugfixあげてます isconnectedto パターン不備 #4

range.rs

// 自身しか関わらないもの
pub trait SelfRange {
  fn new(lower: i8, upper: i8) -> Self;
  fn to_string(&self) -> String;
  fn contains(&self, number: i8) -> bool;
}

// 他のstructが絡むもの
pub trait MultiRange<T> {
  fn equals(&self, range: &T) -> bool;
  fn is_connected_to(&self, range: &T) -> bool;
}

closed_range.rs

// 他のStructが絡む部分の実装

impl MultiRange<OpenRange> for ClosedRange {
  fn equals(&self, _: &OpenRange) -> bool {
    false
  }

  fn is_connected_to(&self, range: &OpenRange) -> bool {
    match range {
      r if self.lower < r.upper && r.upper < self.upper => true,
      r if self.lower < r.lower && r.lower < self.upper => true,
      _ => false,
    }
  }
}

impl MultiRange<ClosedRange> for ClosedRange {
  fn equals(&self, closed_range: &ClosedRange) -> bool {
    self.lower == closed_range.lower && self.upper == closed_range.upper
  }

  fn is_connected_to(&self, range: &ClosedRange) -> bool {
    match range {
      r if self.lower <= r.upper && r.upper <= self.upper => true,
      r if self.lower <= r.lower && r.lower <= self.upper => true,
      _ => false,
    }
  }
}

上記のように、equals()is_connected_toに関して、引数が OpenRange.ver と ClosedRange.ver 共に実装することが可能となっています。

今回の実装の Pull Request

課題3:閉区間と開区間 by h0ng0yut0 · Pull Request #3 · h0ng0yut0/tddbc-rust-practice

次回:TDDBC「課題4:閉区間/開区間への機能追加」 実施予定

今回は、ジェネリクスとその利用方法、またその1例としてのオーバーロード を学べました。次回の課題は何が学べるのか楽しみです。


[これから読んでみたい本]


一緒に様々なことを学んでいく仲間を募集しています。

このサイトのお問い合わせなどからご連絡いただけると幸いです。