Programming Languages - Slices
A slice is a view into a contiguous sequence of elements — it has a pointer, a length, and optionally a capacity. Unlike arrays, slices don't own their data and carry no fixed size in their type signature, making them the idiomatic way to pass sequences in Go, Rust, and C++.
Concept: Array vs Slice
| Array | Slice | |
|---|---|---|
| Size | Fixed, part of the type ([3]int ≠ [4]int) |
Dynamic, not part of the type |
| Ownership | Owns its data | Borrows/views existing data |
| Pass to function | Copies entire array | Only copies pointer + length |
| Typical use | Fixed-size buffers, stack data | Function parameters, subranges |
Go
In Go, a slice is a struct with three fields: a pointer to the underlying array, a length, and a capacity.
// Array (fixed size — part of the type)
a := [3]int{1, 2, 3}
// Slice literal (no size — backed by a new array)
s := []int{1, 2, 3}
// Slice from array or another slice
sub := a[1:3] // elements at index 1 and 2: {2, 3}
// make(type, len, cap)
s2 := make([]int, 5) // len=5, cap=5, zero-valued
s3 := make([]int, 3, 10) // len=3, cap=10
Key behaviors:
s := []int{1, 2, 3, 4, 5}
fmt.Println(len(s), cap(s)) // 5, 5
// Append grows the slice (may reallocate)
s = append(s, 6)
// Slicing shares the underlying array!
a := s[1:3]
a[0] = 99 // also modifies s[1]
fmt.Println(s[1]) // 99
// To get an independent copy:
b := make([]int, len(s[1:3]))
copy(b, s[1:3])
Gotchas:
- Appending to a slice after slicing can overwrite the original if capacity allows
nilslice (var s []int) is valid and haslen=0, cap=0; it's not the same as an empty slice ([]int{}) but behaves identically in most operations
Rust
Rust's slice type is &[T] (immutable) or &mut [T] (mutable). It's a fat pointer: data pointer + length.
// Array (stack-allocated, fixed size)
let a: [i32; 5] = [1, 2, 3, 4, 5];
// Slice reference (borrows the array)
let s: &[i32] = &a;
let sub: &[i32] = &a[1..3]; // elements at index 1 and 2
// Vec<T> can be dereferenced into a slice
let v: Vec<i32> = vec![1, 2, 3];
let sv: &[i32] = &v;
Prefer &[T] over &Vec<T> in function parameters:
// Less flexible — only accepts Vec
fn sum(v: &Vec<i32>) -> i32 { v.iter().sum() }
// More flexible — accepts Vec, array, slice
fn sum(s: &[i32]) -> i32 { s.iter().sum() }
// Callers:
sum(&vec![1, 2, 3]);
sum(&[1, 2, 3]);
sum(&arr[1..3]);
Mutable slices:
let mut a = [3, 1, 4, 1, 5];
let s: &mut [i32] = &mut a[..];
s.sort();
println!("{:?}", a); // [1, 1, 3, 4, 5]
Gotchas:
- Rust's borrow checker prevents mutable aliasing — you can't hold a
&mut [T]and a&[T]to overlapping regions simultaneously - Splitting a mutable slice: use
split_at_mutto get two non-overlapping mutable halves
C++
C++ doesn't have a built-in slice type in older standards. Use std::span (C++20) or absl::Span (pre-C++20).
std::span<T> (C++20):
#include <span>
#include <vector>
#include <iostream>
void print(std::span<const int> s) {
for (int x : s) std::cout << x << ' ';
}
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
print(v); // works with vector
print({v.data() + 1, 3}); // subspan: {2, 3, 4}
int arr[] = {10, 20, 30};
print(arr); // works with C array
}
std::span<const T> is to std::vector<T> what std::string_view is to std::string — a non-owning, lightweight view.
absl::Span (Abseil, pre-C++20):
#include "absl/types/span.h"
void process(absl::Span<const int> data) { ... }
See Abseil Tip #93 for rationale.
Gotchas:
std::spandoes not bounds-check by default in release builds — out-of-range access is UB- Never store a
spanto a temporary — it must not outlive the data it references
Python
Python doesn't have a slice type in the same sense, but slice notation creates new list/array objects. For zero-copy views, use memoryview.
a = [1, 2, 3, 4, 5]
# Slice notation (creates a copy)
s = a[1:4] # [2, 3, 4]
s = a[::2] # [1, 3, 5] (every other)
s = a[::-1] # [5, 4, 3, 2, 1] (reversed)
# memoryview — zero-copy view into bytes/bytearray
data = bytearray(b"hello world")
view = memoryview(data)
sub = view[6:] # no copy
sub[0] = ord('W') # modifies original
print(data) # bytearray(b'hello World')
NumPy arrays (true zero-copy slices):
import numpy as np
a = np.array([1, 2, 3, 4, 5])
s = a[1:4] # view, NOT a copy
s[0] = 99 # modifies a[1]
print(a) # [1, 99, 3, 4, 5]
# Force a copy
s2 = a[1:4].copy()
- Overview
- Which Programming Languages To Learn
- Compile Time and Runtime
- Standard Libraries
- Build Tools
- Memory Management
- Zero Cost Abstractions
- Design Patterns
- Constructors
- Compatibility
- Closures
- Functional Programming
- Generics
- String Interning
- Coding Conventions
- Main
- Ranges
- Lambda
- Random
- IO
- Operators
- Built-in
- Constants
- Functions
- Generics
- Print
- Pointers
- Annotations
- Quotes
- Destructuring
- Generators
- Imports
- Iterators
- Testing
- Visibility
- Assignment Semantics
- Slices
- SSA
- Versus

