count – 1 が最後のインデックスかなと思ったら endIndexなるものがあるので。使おうかなと思ったら
endIndex は 最後のindex + 1だった。ほー
そこで少し見たら ArrayではなくArraySliceになるとインデックスとcountがずれる可能性があると。
スライス(切り出し)を行った際に、インデックスが 「0 からリセットされる言語」 と 「元のインデックスが維持される言語(リセットされない言語)」 を明確に分類して整理しました。
結論から言うと、「Swiftのようにスライス後も元のインデックス(1や2…)がそのまま残る言語」は非常に珍しい(例外的) です。ほとんどの言語は 0 にリセットされるか、コピーを作成して 0 始まりになります。
1. 0 にリセットされる言語(主流)
スライスを行っても、切り出したビュー(またはコピー)の先頭インデックスは必ず 0 から始まります。
そのため、これらの言語では 0 や length - 1(または len - 1)を基準に処理してもインデックス範囲外エラーにはなりません。
🔹 参照(ビュー)を共有するが 0 リセットされる言語
メモリのコピーを作らず軽量に参照だけを切り出しますが、先頭は 0 に再マッピングされる仕様です。
- Go言語
s := array[1:3]とすると、s[0]が元のarray[1]を指します。
- Rust
let slice = &vec[1..3];とすると、slice[0]が元のvec[1]を指します。
- C++ (
std::span/std::string_view)std::span sub = span.subspan(1, 2);とすると、sub[0]が切り出し先頭要素になります。
- Kotlin (
List.subList)val sub = list.subList(1, 3)とすると、ビュー(参照)ですがsub[0]でアクセスします。
- Java (
List.subList)List<String> sub = list.subList(1, 3);とすると、ビューですがsub.get(0)でアクセスします。
🔹 コピー(新しい配列)を作成して 0 リセットされる言語
参照の共有すら発生せず、メモリ上に「新しい配列」を生成するため、当然 0 始まりになります。
- Python (
list[1:3]) - JavaScript / TypeScript (
array.slice(1, 3)) - Ruby (
array[1..2]) - PHP (
array_slice($array, 1, 2)) ※デフォルト挙動
2. 元のインデックスが維持される(0 にリセットされない)言語・仕様
スライスしても「元の配列のインデックス番号」をそのまま保持するため、0 や length - 1 を盲信するとクラッシュ・バグになる言語です。
- Swift (
ArraySlice)let slice = array[1...2]とすると、slice[1]から始まります(slice[0]は存在しない)。
- PHP (
array_sliceで第4引数にtrueを渡した場合)array_slice($array, 1, 2, true)とすると、元のキー/インデックス(1や2…)がそのまま維持されます。
- Julia (
@viewsまたは 部分配列)- 標準の切り出しはコピーで
1始まり(Juliaは1始まりの言語)ですが、view機能(@views)を使うと、元の多次元配列などのインデックス範囲(例:3:5)をそのまま維持する設定が可能です。
- 標準の切り出しはコピーで
一覧比較テーブル
| 言語 | 操作 / 型 | スライスの実体 | 先頭インデックス | count – 1 や 0 判定の安全性 |
| Swift | ArraySlice | 参照ビュー | 元のまま (例: 1) | ❌ 危険 (startIndex 必須) |
| Go | []T (Slice) | 参照ビュー | 0 にリセット | ⭕️ 安全 |
| Rust | &[T] (Slice) | 参照ビュー | 0 にリセット | ⭕️ 安全 |
| C++ | std::span | 参照ビュー | 0 にリセット | ⭕️ 安全 |
| Java / Kotlin | subList() | 参照ビュー | 0 にリセット | ⭕️ 安全 |
| Python | list[a:b] | 新規コピー | 0 にリセット | ⭕️ 安全 |
| JavaScript | array.slice() | 新規コピー | 0 にリセット | ⭕️ 安全 |
なぜこんな違いがあるのか?
- 他の言語(Go, Rust, Python等):「切り出したもの(スライス)は、それ自体を1つの独立したリスト/配列として扱いたい」という直感的な扱いやすさを重視しています。
- Swift:「元の配列とスライスされた部分で、同じインデックス番号を使って相互に位置を追跡・参照できるようにする」という、アルゴリズムの厳密性と安全性を最重視した独自の設計思想を採用しています。
そのため、他言語から Swift に来た開発者ほど、この ArraySlice の「インデックスが維持される挙動」に引っかかりやすくなっています。Swift を扱う際だけは、特に startIndex / endIndex の意識を強く持っておくのが正解です。
なかなか面白いですね?


コメント