Array.blit — Copy Subarray
Tutorial
The Problem
Copy a contiguous sub-range of one array into another array at a specified offset. The OCaml primitive Array.blit src src_pos dst dst_pos len copies len elements starting at src_pos in src into dst beginning at dst_pos.
🎯 Learning Outcomes
copy_from_slice provides the same bulk-copy primitive as Array.blit&arr[a..b] is the direct analogue of OCaml's src_pos/len pair&[T] (immutable source) and &mut [T] (mutable destination) makes aliasing impossible at compile timeVec shows how to avoid in-place mutation while keeping clarity🦀 The Rust Way
Rust encodes the mutability contract in the type: the destination must be &mut [T] while the source is &[T]. Sub-ranges are expressed via slice indices [start..end], and copy_from_slice performs the bulk copy in one call. The T: Copy bound ensures the element type is cheaply bitwise-copyable, mirroring OCaml's Array.blit which works on any value type.
Code Example
pub fn array_blit<T: Copy>(
src: &[T], src_pos: usize,
dst: &mut [T], dst_pos: usize,
len: usize,
) {
dst[dst_pos..dst_pos + len].copy_from_slice(&src[src_pos..src_pos + len]);
}Key Differences
&mut [T] explicitly.(src_pos, len) pair; Rust uses slice range [pos..pos+len].unsafe.Vec<T> instead of mutating, making the operation pure at the cost of an allocation.OCaml Approach
Array.blit is OCaml's built-in for block array copies. It is imperative by nature: it mutates dst directly. The type system does not enforce source/destination separation — both arguments are 'a array, and the programmer must ensure no aliasing.
Full Source
/// Idiomatic Rust: use slice::copy_from_slice with subslice indexing.
/// Copies `len` elements from `src[src_pos..]` into `dst[dst_pos..]`.
///
/// # Panics
/// Panics if the source or destination slices are out of bounds.
pub fn array_blit<T: Copy>(src: &[T], src_pos: usize, dst: &mut [T], dst_pos: usize, len: usize) {
dst[dst_pos..dst_pos + len].copy_from_slice(&src[src_pos..src_pos + len]);
}
/// Functional style: build the result without mutating in place.
/// Returns a new Vec with the blitted region applied.
pub fn array_blit_functional<T: Copy>(
src: &[T],
src_pos: usize,
dst: &[T],
dst_pos: usize,
len: usize,
) -> Vec<T> {
let mut result = dst.to_vec();
result[dst_pos..dst_pos + len].copy_from_slice(&src[src_pos..src_pos + len]);
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blit_middle() {
let src = [10, 20, 30, 40, 50];
let mut dst = [0i32; 8];
array_blit(&src, 1, &mut dst, 2, 3);
assert_eq!(dst, [0, 0, 20, 30, 40, 0, 0, 0]);
}
#[test]
fn test_blit_start_to_start() {
let src = [1, 2, 3];
let mut dst = [0i32; 3];
array_blit(&src, 0, &mut dst, 0, 3);
assert_eq!(dst, [1, 2, 3]);
}
#[test]
fn test_blit_single_element() {
let src = [99];
let mut dst = [0i32; 5];
array_blit(&src, 0, &mut dst, 3, 1);
assert_eq!(dst, [0, 0, 0, 99, 0]);
}
#[test]
fn test_blit_zero_length() {
let src = [1, 2, 3];
let mut dst = [7i32; 4];
array_blit(&src, 0, &mut dst, 0, 0);
assert_eq!(dst, [7, 7, 7, 7]);
}
#[test]
fn test_functional_blit() {
let src = [10, 20, 30, 40, 50];
let dst = [0i32; 8];
let result = array_blit_functional(&src, 1, &dst, 2, 3);
assert_eq!(result, vec![0, 0, 20, 30, 40, 0, 0, 0]);
}
#[test]
fn test_functional_blit_does_not_mutate_original() {
let src = [10, 20, 30];
let dst = [0i32; 5];
let result = array_blit_functional(&src, 0, &dst, 1, 3);
assert_eq!(result, vec![0, 10, 20, 30, 0]);
assert_eq!(dst, [0, 0, 0, 0, 0]);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blit_middle() {
let src = [10, 20, 30, 40, 50];
let mut dst = [0i32; 8];
array_blit(&src, 1, &mut dst, 2, 3);
assert_eq!(dst, [0, 0, 20, 30, 40, 0, 0, 0]);
}
#[test]
fn test_blit_start_to_start() {
let src = [1, 2, 3];
let mut dst = [0i32; 3];
array_blit(&src, 0, &mut dst, 0, 3);
assert_eq!(dst, [1, 2, 3]);
}
#[test]
fn test_blit_single_element() {
let src = [99];
let mut dst = [0i32; 5];
array_blit(&src, 0, &mut dst, 3, 1);
assert_eq!(dst, [0, 0, 0, 99, 0]);
}
#[test]
fn test_blit_zero_length() {
let src = [1, 2, 3];
let mut dst = [7i32; 4];
array_blit(&src, 0, &mut dst, 0, 0);
assert_eq!(dst, [7, 7, 7, 7]);
}
#[test]
fn test_functional_blit() {
let src = [10, 20, 30, 40, 50];
let dst = [0i32; 8];
let result = array_blit_functional(&src, 1, &dst, 2, 3);
assert_eq!(result, vec![0, 0, 20, 30, 40, 0, 0, 0]);
}
#[test]
fn test_functional_blit_does_not_mutate_original() {
let src = [10, 20, 30];
let dst = [0i32; 5];
let result = array_blit_functional(&src, 0, &dst, 1, 3);
assert_eq!(result, vec![0, 10, 20, 30, 0]);
assert_eq!(dst, [0, 0, 0, 0, 0]);
}
}
Deep Comparison
OCaml vs Rust: Array.blit — Copy Subarray
Side-by-Side Code
OCaml
let src = [| 10; 20; 30; 40; 50 |]
let dst = Array.make 8 0
let () = Array.blit src 1 dst 2 3
(* dst is now [| 0; 0; 20; 30; 40; 0; 0; 0 |] *)
Rust (idiomatic — in-place)
pub fn array_blit<T: Copy>(
src: &[T], src_pos: usize,
dst: &mut [T], dst_pos: usize,
len: usize,
) {
dst[dst_pos..dst_pos + len].copy_from_slice(&src[src_pos..src_pos + len]);
}
Rust (functional — returns new Vec)
pub fn array_blit_functional<T: Copy>(
src: &[T], src_pos: usize,
dst: &[T], dst_pos: usize,
len: usize,
) -> Vec<T> {
let mut result = dst.to_vec();
result[dst_pos..dst_pos + len].copy_from_slice(&src[src_pos..src_pos + len]);
result
}
Type Signatures
| Concept | OCaml | Rust |
|---|---|---|
| Function signature | val blit : 'a array -> int -> 'a array -> int -> int -> unit | fn array_blit<T: Copy>(src: &[T], src_pos: usize, dst: &mut [T], dst_pos: usize, len: usize) |
| Source array | 'a array | &[T] (immutable slice) |
| Destination array | 'a array (mutable by convention) | &mut [T] (enforced mutable slice) |
| Sub-range | (src_pos, len) pair of int | [src_pos..src_pos + len] slice range |
| Element constraint | none (structural equality) | T: Copy (bitwise copyable) |
Key Insights
(src_pos, len) pair maps directly to Rust's [start..start+len] range syntax — both express the same sub-array window, but Rust's version is first-class and bounds-checked.&[T] (read-only) from &mut [T] (writable). The compiler ensures the borrow checker prevents using the same allocation as both source and destination without unsafe.T: Copy replaces structural equality:** OCaml's Array.blit works on any type because OCaml arrays are GC-managed references. Rust's copy_from_slice requires T: Copy to guarantee a bitwise memcpy is safe and correct.copy_from_slice is one function call:** The standard library maps to the same hardware instruction (a block memory copy) that OCaml's Array.blit ultimately uses — no element-by-element loop in either language.Vec<T> (allocate + copy + overwrite window) is idiomatic for callers that prefer immutability. OCaml would require Array.copy followed by Array.blit.When to Use Each Style
**Use idiomatic Rust (in-place &mut [T]):** when you control the destination buffer and want zero allocation — e.g., filling a pre-allocated frame buffer, copying into a network packet, or updating a region of a large array without cloning.
**Use functional Rust (returns Vec<T>):** when the caller should not be responsible for allocating the destination, when you want a pure function that is easier to test, or when the destination may be referenced elsewhere and mutation would be unsafe.