「TDDBCの課題をRustで実施し、言語仕様を学んでいこう!」というシリーズの記事です。
Rustの記事シリーズは以下となっています。
[ひとことで言うとこんな記事]
以下を学ぶ事ができます。
[目次]
事前にcontains
という実装をしているので、すべての値に対してcontainsすればよいとの考えました。
fn contains_all(&self, numbers: Vec<i8>) -> bool {
for number in numbers {
if !self.contains(number) {
return false;
}
}
return true;
}
結局これが、 どのStructであっても同じ実装 となることがわかったので、 trait自身にデフォルトで記述してしまうことにしました 。
pub trait SelfRange {
fn new(lower: i8, upper: i8) -> Self;
fn to_string(&self) -> String;
fn contains(&self, number: i8) -> bool;
fn contains_all(&self, numbers: Vec<i8>) -> bool {
for number in numbers {
if !self.contains(number) {
return false;
}
}
return true;
}
fn parse(string: String) -> Self;
}
普通に課題4-1を実装してみました。なんだか、is_connected_toとget_intersectionが似たような感じに思えたので、共通化してみようと思いました。
pub trait MultiRange<T> {
fn equals(&self, range: &T) -> bool;
fn is_connected_to(&self, range: &T) -> bool;
fn get_intersection(&self, range: &T) -> String;
fn is_connected_to(&self, range: &T) -> bool;
fn get_intersection(&self, range: &T) -> String;
// New 共通集合があるかどうかをResult型で確認
fn intersection(&self, range: &T) -> Result<String, String>;
}
上記の新しいfnである fn intersection(&self, range: &T) -> Result<String, String>;
を定義することで、 fn is_connected_to(&self, range: &T) -> bool;``fn get_intersection(&self, range: &T) -> String;
を以下のように簡単にしたいと思いました。
fn is_connected_to(&self, range: &T) -> bool {
match self.intersection(range) {
Ok(_) => true,
Err(_) => false,
}
}
fn get_intersection(&self, range: &T) -> String {
self.intersection(range).unwrap()
}
これが、結局 どのStructであっても、どのジェネリクスの指定であっても同じ実装 となることがわかったので、 trait自身にデフォルトで記述してしまうことにしました 。
pub trait MultiRange<T> {
fn equals(&self, range: &T) -> bool;
fn is_connected_to(&self, range: &T) -> bool;
fn is_connected_to(&self, range: &T) -> bool {
match self.intersection(range) {
Ok(_) => true,
Err(_) => false,
}
}
fn get_intersection(&self, range: &T) -> String {
self.intersection(range).unwrap()
}
fn intersection(&self, range: &T) -> Result<String, String>;
}
ジェネリクストレイトでもうまく表現できると、traitに処理を実装することができますね。
1.Cargo.tomlの[dependencies]にregexを追加
Cargo.toml
[dependencies]
regex = "0.1"
2.利用する箇所に以下の記述を追加
OpenRange.rs等
extern crate regex;
use regex::Regex;
3.Regexを以下のように利用する
fn parse(string: String) -> OpenRange {
let regex = Regex::new(r"\((\d+),(\d+)\)").unwrap();
let caps = regex.captures(&string).unwrap();
Self::new(
caps.at(1).unwrap().parse().unwrap(),
caps.at(2).unwrap().parse().unwrap(),
)
}
単純にcapture
したものの中身は以下のようになっています。
// 正規表現にマッチしなかった場合
None
// 正規表現にマッチした場合
Some(Captures({
0: Some("(1,5)"),
1: Some("1"), // ← 1つ目の (\d+) の値
2: Some("5"), // ← 2つ目の (\d+) の値
})),
Capturesにはat
というメソッドが準備されており、at(1)
などと記述することで1つ目の値が取得できたりします。
const string = "(1,5)"
let regex = Regex::new(r"\((\d+),(\d+)\)").unwrap();
let caps = regex.captures(&string).unwrap(); // 上記 Captures の取得
caps.at(1); // 1つ目の Some("1")が取得できる
parse系だったりregex系はResultだったりOptionだったりで返却されることが他の言語でも多いので、想像しやすいとは思います。
正規表現テスターサイト
課題4: 閉区間/開区間への機能追加 by h0ng0yut0 · Pull Request #5 · h0ng0yut0/tddbc-rust-practice
一旦このへんでTDD BCでRustをまなぶシリーズを終了したいとおもいます。
基本的な記述方法等がわかったのですが、そこまで頑張らなくてもいいかと思ってあきらめた点が何点か。
なにか参考になるもの等がありましたら、ご連絡いただけると幸いです。
[本屋で見たRust本(買おうか悩み中)]
__一緒に様々なことを学んでいく仲間を募集__しています。
このサイトのお問い合わせなどからご連絡いただけると幸いです。