Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

スライス

コレクションの一部を参照する「スライス」について学びます。

スライスとは

スライスはコレクションの一部への参照です。所有権を持ちません。

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

    let hello = &s[0..5];   // "hello"へのスライス
    let world = &s[6..11];  // "world"へのスライス

    println!("{}, {}", hello, world);
}

文字列スライス(&str)

基本的な使い方

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

    let slice1 = &s[0..5];   // "hello"
    let slice2 = &s[6..11];  // "world"
    let slice3 = &s[0..];    // "hello world"(最初から)
    let slice4 = &s[..5];    // "hello"(最初から5まで)
    let slice5 = &s[..];     // "hello world"(全体)

    println!("{}", slice1);
}

範囲の構文

構文意味例(“hello“の場合)
[0..5]0から5未満“hello”
[..5]最初から5未満“hello”
[3..]3から最後まで“lo”
[..]全体“hello”
[0..=4]0から4まで(含む)“hello”

メモリレイアウト

#![allow(unused)]
fn main() {
let s = String::from("hello world");
let hello = &s[0..5];

String (s)                   ヒープ
┌─────────────┐            ┌───────────────────────┐
│ ptr ─────────────────────→│ h e l l o   w o r l d │
│ len: 11     │            └───────────────────────┘
│ cap: 11     │                ↑
└─────────────┘                │ ptr
                            ┌──┴────────┐
&str (hello)               │ len: 5    │
                           └───────────┘
}

&strは以下を持ちます:

  • ポインタ(データの開始位置)
  • 長さ

String と &str の関係

#![allow(unused)]
fn main() {
let s: String = String::from("hello");  // 所有権あり
let slice: &str = &s;                   // Stringへのスライス
let literal: &str = "hello";            // 文字列リテラル('static)
}

使い分け

特徴用途
String所有権あり、変更可能文字列を所有・変更したいとき
&str参照、読み取り専用文字列を読むだけのとき

関数の引数は&strを推奨

#![allow(unused)]
fn main() {
// これより...
fn greet(name: &String) { ... }

// こちらが良い
fn greet(name: &str) { ... }
}

&strを受け取る関数は、Stringからも文字列リテラルからも呼び出せます:

fn greet(name: &str) {
    println!("Hello, {}!", name);
}

fn main() {
    let s = String::from("World");

    greet(&s);        // Stringから(自動的に&strに変換)
    greet("World");   // 文字列リテラルから
}

配列スライス

配列やVecの一部も参照できます。

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let slice: &[i32] = &arr[1..4];  // [2, 3, 4]

    println!("{:?}", slice);
}

Vecのスライス

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    let slice = &v[1..4];  // &[i32]型

    println!("{:?}", slice);  // [2, 3, 4]
}

スライスを使った関数

最初の単語を返す

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

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

    &s[..]
}

fn main() {
    let s = String::from("hello world");
    let word = first_word(&s);
    println!("最初の単語: {}", word);  // "hello"
}

配列の合計を計算

fn sum(numbers: &[i32]) -> i32 {
    let mut total = 0;
    for n in numbers {
        total += n;
    }
    total
}

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let vec = vec![10, 20, 30];

    println!("配列の合計: {}", sum(&arr));      // 15
    println!("Vecの合計: {}", sum(&vec));       // 60
    println!("一部の合計: {}", sum(&arr[1..4])); // 9 (2+3+4)
}

スライスの安全性

スライスは参照なので、所有権ルールに従います。

fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);  // 不変借用

    // s.clear();  // エラー!wordが有効な間は変更できない

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

fn first_word(s: &str) -> &str {
    &s[..5]
}

よくあるパターン

パターン1: イテレーションしながらインデックスを使わない

#![allow(unused)]
fn main() {
// 良くない(インデックスアクセス)
fn process_bad(data: &[i32]) {
    for i in 0..data.len() {
        println!("{}", data[i]);
    }
}

// 良い(イテレータ使用)
fn process_good(data: &[i32]) {
    for item in data {
        println!("{}", item);
    }
}
}

パターン2: 範囲チェック

#![allow(unused)]
fn main() {
fn safe_get(data: &[i32], index: usize) -> Option<&i32> {
    data.get(index)  // 範囲外ならNone
}
}

まとめ

スライス型元のデータ用途
&strString, 文字列リテラル文字列の一部を参照
&[T]配列, Vec<T>コレクションの一部を参照
特徴内容
所有権なし(参照)
サイズポインタ + 長さ
安全性借用ルールに従う

確認テスト

Q1. &strStringの違いは?

Q2. 関数の引数として文字列を受け取る場合、推奨される型は?

Q3. 以下のコードの出力は?
let s = String::from("hello world"); let part1 = &s[..5]; let part2 = &s[6..]; println!("{}-{}", part1, part2);

Q4. let mut s = String::from("hello"); let slice = &s[..]; s.push_str(" world");がエラーになる理由は?

Q5. スライス&[T]の特徴として正しいものは?


Phase 2 完了!

おめでとうございます!Rust最重要のPhase 2を完了しました。

学んだこと:

  • スタックとヒープの違い
  • 所有権の3つのルール
  • 参照と借用(不変参照、可変参照)
  • ライフタイムの基礎
  • スライス(&str、&[T])

**これらの概念はRustの核心です。**最初は難しく感じても、コードを書いていくうちに自然と理解できるようになります。

次のPhase: Phase 3: 構造化プログラミング