ExamplesBy LevelBy TopicLearning Paths
1183 Intermediate

Array.blit — Copy Subarray

Functional Programming

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

  • • How Rust's copy_from_slice provides the same bulk-copy primitive as Array.blit
  • • Slice sub-range syntax &arr[a..b] is the direct analogue of OCaml's src_pos/len pair
  • • The ownership split between &[T] (immutable source) and &mut [T] (mutable destination) makes aliasing impossible at compile time
  • • A functional alternative using Vec 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

  • Mutability: OCaml arrays are always mutable; Rust requires &mut [T] explicitly.
  • Bounds expression: OCaml uses (src_pos, len) pair; Rust uses slice range [pos..pos+len].
  • Aliasing safety: Rust's borrow checker prevents passing the same array as both source and destination without unsafe.
  • Functional alternative: Rust can produce a new 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]);
        }
    }
    ✓ Tests Rust test suite
    #[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

    ConceptOCamlRust
    Function signatureval blit : 'a array -> int -> 'a array -> int -> int -> unitfn 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 constraintnone (structural equality)T: Copy (bitwise copyable)

    Key Insights

  • Slice ranges unify position + length: OCaml's (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.
  • Mutability is encoded in the type: OCaml arrays are always mutable; Rust distinguishes &[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.
  • Functional alternative is a first-class pattern: Because Rust's slice API is composable, returning a new 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.

    Open Source Repos