グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



安全性、速度、並行性を兼ね備えた言語と、巷でうわさの「Rust」を覗いてみる(その9:スライス型)

みなさまお久しぶりです。

今回は「The Rust Programming Language」を読みながら、 スライス型ついてみていきたいとおもいます。

それでは、解説していきます!

スライス型

スライスはコレクションの一部の要素の参照のようです。
参照なので所有権はありません。

fn first_word(s: &String) -> usize {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }

    s.len()
}

このコードは、与えられた文字列の中の最初の空白を探し、そのインデックスを返します。

fn main() {
    let mut s = String::from("hello world");

    let word = first_word(&s);

    println!("{}", word); // 5

    println!("{}", s.chars().nth(word).unwrap()); // 空白文字
}

実際に動かしてみると、「5」を返すことがわかります。

次に、文字を取得するまえに「s」に対してclearメソッドを呼び出し、Stringを空にします。

fn main() {
    let mut s = String::from("hello world");

    let word = first_word(&s);

    println!("{}", word); // 5

    s.clear();

    println!("{}", s.chars().nth(word).unwrap()); // エラー
}
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:10:40
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

コンパイルはとおりますが、実行するとエラーになります。

これは、自分でclearしているのでエラーになるのは当然なのですが、元のStringのデータとインデックスのデータが関連付けられていないので、自分で管理しなくてはならず、 意図せぬエラーにつながる可能性があります。

文字列スライス

文字列スライスはStringの一部分の参照です。

fn main() {
    let mut s = String::from("hello world");

    println!("{}", &s[0..5]); // hello 
    println!("{}", &s[6..11]); // world 
}

インデックスが、「0」から「5-1」の部分参照を取得できます。
終了インデックスの指定は、実際のインデックスより1大きい値を指定することに注意が必要です。
最初これで戸惑いました。

最初の関数を改良する

最初の関数を、文字列スライスを使うことで改良します。

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

戻り値を「usize」から「&str」に変えて、文字列スライスを返すようにします。

fn main() {
    let mut s = String::from("hello world");

    println!("{}", first_word(&s)); // hello
}

「hello」が取得出来ているのが確認できます。

エラーになるのを確認する

では、改良前の関数と同様に、「s」の「clear」メソッドを呼びたした後に文字列スライスを表示してみます。

fn main() {
    let mut s = String::from("hello world");

    let word = first_word(&s);

    s.clear(); // ここでエラーになる

    println!("the first word is: {}", word);
}

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
 --> src/main.rs:6:5
  |
4 |     let word = first_word(&s);
  |                           -- immutable borrow occurs here
5 |
6 |     s.clear();
  |     ^^^^^^^^^ mutable borrow occurs here
7 |
8 |     println!("the first word is: {}", word);
  |                                       ---- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `rust-sandbox` due to previous error

エラーになりました。

これは、「うわさのRustを覗いてみる(その7)」で見た、参照の制約に違反しています。 不変の参照を取得した後に、さらに可変な参照を取得できないのです。
このように、文字列スライスを利用することで、予期しないエラーを検出することができます。
最初の関数の「usize」のように、元の文字列とまったく別の値を作ってしまうと、 文字列の変更に対応できなくなります。

しかし、文字列スライスのような参照をうまく利用することで、元の文字列と常に関連付けられた 状態として扱うことができます。
なぜなら、参照の取り扱いはRustの制約に守られており、データの競合が発生しないようになっているからです。

この記事を書いた人

tkr2f
tkr2f事業開発部 web application engineer
2008年にアーティスへ入社。
システムエンジニアとして、SI案件のシステム開発に携わる。
その後、事業開発部の立ち上げから自社サービスの開発、保守をメインに従事。
ドメイン駆動設計(DDD)を中心にドメインを重視しながら、保守可能なソフトウェア開発を探求している。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします