導入
文字列や配列の一部分だけを参照したいことがあります。「先頭の単語だけ」「配列の2〜4番目だけ」。このとき、コピーせず・所有権も奪わず、元データの一部を借りるのが スライス(slice) です。スライスも参照の一種なので、借用のルールがそのまま効きます。
説明
文字列スライス &str は、String の一部(または全部)を指す参照です。
fn main() {
let s = String::from("hello world");
let hello = &s[0..5]; // 0番目から5番目の手前まで → "hello"
let world = &s[6..11]; // 6番目から11番目の手前まで → "world"
println!("{hello} / {world}");
}
&s[0..5]…sの一部を指すスライス。新しい文字列を作るのではなく、sのヒープ上の一部を借りるだけ。コピーは発生しません。- スライスは内部的に「開始位置へのポインタ」と「長さ」を持ちます。
graph LR
subgraph stack["スタック"]
s["s: String(所有者)"]
hello["hello: &str<br/>ptr=先頭, len=5"]
end
subgraph heap["ヒープ(s が所有)"]
d["h e l l o (空白) w o r l d"]
end
s --> d
hello -.->|一部を指す| d
実は、第1章から使ってきた文字列リテラル "hello" の型がまさに &str(プログラムに埋め込まれたデータへのスライス)でした。だから関数の引数は &String より &str にしておくと、String でもリテラルでも受け取れて便利です。
fn first_word(text: &str) -> &str { // &str なら String もリテラルも渡せる
for (i, c) in text.char_indices() {
if c == ' ' {
return &text[0..i]; // 最初の空白の手前までを借りて返す
}
}
text
}
配列スライス も同じ考え方です。
fn main() {
let arr = [10, 20, 30, 40, 50];
let middle = &arr[1..4]; // [20, 30, 40] を指すスライス(型は &[i32])
println!("{:?}", middle);
}
スライスは「所有権を持たず、元データの一部を借りる参照」。だからこそ元データが生きている間しか使えません。「借りたものは、貸し主より長生きできない」——このルールを追跡するのが、次のライフタイムです。上の first_word の戻り値 &str も、引数 text が生きている間だけ有効です。それをコンパイラはどう知るのでしょう?
試すには
&s[0..5] のスライスを作ったあと、s を書き換えたり手放したりしようとすると、「スライスが s を借用中だ」とコンパイラが止めます。スライスも借用の一種であり、借用ルールに従うことを確認しておきましょう。