本文へスキップ
BecomeCoder

Rustコース · 第6章 スライスとライフタイム · レッスン23

スライス ― 一部を借りる

ブラウザで完結

導入

文字列配列一部分だけを参照したいことがあります。「先頭の単語だけ」「配列の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 を借用中だ」とコンパイラが止めます。スライスも借用の一種であり、借用ルールに従うことを確認しておきましょう。

実際に動かしてみよう

下のエディタにRustを書いて「実行」を押すと、学習用シミュレータが println! の出力を表示します(本物のrustcではなく、教材の範囲を再現した軽量エンジンです)。struct・enum・match・トレイト・Vec/String/HashMap・Option/Result なども動きます。本文の例を書き換えて動かしてみましょう(所有権・借用チェッカーのコンパイルエラーは再現しないため、動きの確認用として使ってください)。

Rust — ブラウザ内で実行(学習用シミュレータ)

Rustの教材サブセットを動かす学習用シミュレータを読み込みます(本物のrustcではなく、動きを再現した軽量な自作エンジンです)。
スクロールして表示された時点でも自動で読み込まれます。