Rust 入門 簡単な関数を書いてみる(2)

Rust 入門つづき。

前回、簡単な関数のサンプルとして住所から都道府県を切り出すトコロを書いたので、次は市区町村を切り出す関数。

その前に、前回は関数の戻り値として Option を返すようにしていたのだが、良く考えるとあまり意味がなく冗長なだけなので、単純にタプルを返すように修正。 今回作成する市区町村を切り出す関数も同様の仕様とする。

とりあえず、以前に書いたC#のコードを元に書いてはみたが、変数 re2 を用いた正規表現のチェックを2か所でやってるのがイマイチ。何か巧いやり方はないものか。

Rust の String と &str の使い分けは初学者にはややこしい。イメージとしては String は一般的な文字列型、&str は文字列のスライスと(今のところ)理解していて、 関数の引数には &str で戻り値には String ぐらいのザックリとした感覚で書いている。(最初、&str を戻り値で返そうとしたら怒られた)

今回、ちょっと引っかかったのは、

let city: &str;
if let Some(m) = re.captures(address) {
  city = &m[1]
}

というような感じのコードを書いたら

borrowed value does not live long enoughrustc(E0597)

と怒られたことで、変数mの値はif文のブロックを抜けた時点で破棄されるため、参照をブロックの外に持ち出しちゃダメよということらしい。
当たり前のことだが、まだ身についてないのでついついこういうしょーもないミスをしてしまう。

extern crate regex;
use regex::Regex;

const PREFECTURES:[(&str, &str); 47] = [
        ("01","北海道"), ("02","青森県"), ("03","岩手県"), ("04","宮城県"), ("05","秋田県"),
        ("06","山形県"), ("07","福島県"), ("08","茨城県"), ("09","栃木県"), ("10","群馬県"),
        ("11","埼玉県"), ("12","千葉県"), ("13","東京都"), ("14","神奈川県"), ("15","新潟県"),
        ("16","富山県"), ("17","石川県"), ("18","福井県"), ("19","山梨県"), ("20","長野県"),
        ("21","岐阜県"), ("22","静岡県"), ("23","愛知県"), ("24","三重県"), ("25","滋賀県"),
        ("26","京都府"), ("27","大阪府"), ("28","兵庫県"), ("29","奈良県"), ("30","和歌山県"),
        ("31","鳥取県"), ("32","島根県"), ("33","岡山県"), ("34","広島県"), ("35","山口県"),
        ("36","徳島県"), ("37","香川県"), ("38","愛媛県"), ("39","高知県"), ("40","福岡県"),
        ("41","佐賀県"), ("42","長崎県"), ("43","熊本県"), ("44","大分県"), ("45","宮崎県"),
        ("46","鹿児島県"), ("47","沖縄県")    
    ];

// 住所から都道府県とそれ以降を分割する
fn get_prefecture(address: &str) -> (String, String) {
    let prefectures = PREFECTURES.iter().map(|x| x.1).collect::<Vec<&str>>().join("|");
    let pattern = format!("^({})(.+*)$", prefectures);
    let re = Regex::new(&pattern).unwrap();

    match re.captures(address) {
        Some(m) => ((&m[1]).to_string(), (&m[2]).to_string()),
        None => ("".to_string(), address.to_string())
    }
}

// 住所から市区町村とそれ以降を分割する
fn get_city(address: &str) -> (String, String) {
    let re1 = Regex::new(concat!(
        "^(余市郡(仁木町|赤井川村|余市町)|余市町|柴田郡村田町|(武蔵|東)村山市|",
        "[東西北]村山郡...?町|田村(市|郡..町)芳賀郡市貝町|(佐波郡)?玉村町|[羽大]村市|",
        "(十日|大)町市|(中新川郡)?上市町|(野々|[四廿]日)市市|西八代郡市川三郷町|",
        "神崎郡市川町|高市郡(高取町|明日香村)|(吉野郡)?下市町|(杵島郡)?大町町)(.+)"
    )).unwrap();
    let re2 = Regex::new("^(.+[市区町村])(.+)").unwrap();

    match re1.captures(address) {
        Some(m) => {
            match re2.captures(&m[12]) {
                Some(m2) => (format!("{}{}",(&m[1]),(&m2[1])), (&m2[2]).to_string()),
                None => ((&m[1]).to_string(), (&m[12]).to_string())
            }
        },
        None => {
            match re2.captures(address) {
                Some(m2) => ((&m2[1]).to_string(), (&m2[2]).to_string()),
                None => ("".to_string(), address.to_string())
            }
        }
    }
}



fn main() {
    let (prefecture, tmp1) = get_prefecture("大阪府大阪市北区ほげほげ9丁目99-909");
    let (city, tmp2) = get_city(&tmp1);

    println!("Prefecture: {:?}", prefecture);
    println!("City: {:?}", city);
    println!("...: {:?}", tmp2);
}