スライス(slice、型 [T] および &[T])は、Rustにおいて配列、ベクター、およびその他の要素シーケンスのサブセットに対する安全で効率的なアクセスを提供するために導入されました。これにより、アロケーションやデータの再コピーを回避し、コレクションの一部を「視覚化」または"ウィンドウ"を提供します。これは、コンパイル時にサイズが固定されている配列や、ポインタと長さを保持しますがメモリを所有する動的コレクションとは異なります。
メモリのライフタイムを厳格に制御しない言語での配列やベクターの操作では、しばしば範囲外アクセス(out of bounds)やメモリリーク、不正なポインタの使用などのエラーが発生します。コレクションのサブセットを扱う際には、コピーを避け、メモリの安全性を損なわないことが重要で、特にシステムレベルではそれが求められます。
Rustにおけるスライスは、データの一部に対する「ポインタ + 長さ」であり、内容を所有しません。スライスは常にライフタイムに伴われ、コンパイラはスライスが元のデータ(配列、Vec、String)を超えないことを保証します。すべてのスライス操作は安全なアクセスメソッドを介して行われ、任意の境界を越えることはランタイムエラーを引き起こします。
コード例:
let arr = [1, 2, 3, 4, 5]; let slice = &arr[1..4]; // [2,3,4] 型: &[i32] let mut vec = vec![10, 20, 30]; let mut_slice: &mut [i32] = &mut vec[..2]; mut_slice[0] = 99; assert_eq!(vec, [99, 20, 30]);
主な特徴:
元の配列やベクターのサイズを超えるスライスを作成することはできますか?
いいえ。コンパイラとランタイムは、スライスが元データの有効なインデックスでのみ作成できることを保証します。境界を越えようとするとpanicが発生します。
let arr = [1, 2, 3]; let s = &arr[0..4]; // 実行時にpanic
スライスはメモリの独立した所有者ですか?
いいえ。スライスはデータへの「ウィンドウ」であり、メモリを所有していません。ソースがローカルの場合、関数からスライスを返そうとすると、コンパイル時エラーになります。
fn give_slice() -> &[i32] { let arr = [1,2,3]; &arr[1..] } // エラー: arrは十分に長く生存しない
Rustにおけるスライスと配列の型と操作の違いは何ですか?
配列は、コンパイル時に知られている固定長を持ち、すべてスタックに保存されます。スライスは動的に決定される任意の長さを持ち、常にポインタと長さを保持します。
let a: [u32; 3] = [1,2,3]; // 配列 固定長 let s: &[u32] = &a[..]; // 任意のサイズのスライス
プログラマーがローカル配列からスライスを関数から返しました。関数が終了すると元の配列が削除され、スライスは「ぶら下がり」ポインタになりました。これがバグとなり、さらにはクラッシュを引き起こしました。
プラス:
マイナス:
スライスは常に外部データへの参照として作成され、データの所有者とスライスは同じ期間生存します。コンパイラはスライスとソースの間のライフタイムを密接に関連づけることを保証します。
プラス:
マイナス: