安全性、速度、並行性を兼ね備えた言語と、巷でうわさの「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の制約に守られており、データの競合が発生しないようになっているからです。
この記事を書いた人
-
2008年にアーティスへ入社。
システムエンジニアとして、SI案件のシステム開発に携わる。
その後、事業開発部の立ち上げから自社サービスの開発、保守をメインに従事。
ドメイン駆動設計(DDD)を中心にドメインを重視しながら、保守可能なソフトウェア開発を探求している。
この執筆者の最新記事
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー