CH

check-bounds-safety

Implement safe index and length checking.

Install

mkdir -p .claude/skills/check-bounds-safety && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5678" && unzip -o skill.zip -d .claude/skills/check-bounds-safety && rm skill.zip

Installs to .claude/skills/check-bounds-safety

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Apply type-safe bounds checking patterns using Index/Length types instead of usize. Use when working with arrays, buffers, cursors, viewports, or any code that handles indices and lengths.
188 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Implement type-safe index and length wrappers
  • Validate buffer and array access boundaries
  • Perform cursor positioning and viewport checks
  • Convert VT-100 ranges to Rust ranges

How it works

The skill provides newtype wrappers that enforce compile-time checks, preventing category errors between indices and lengths.

Inputs & outputs

You give it
Raw usize values
You get back
Type-safe Index or Length objects

When to use check-bounds-safety

  • Implementing bounds checking
  • Replacing usize with safe types
  • Validating buffer access

About this skill

Type-Safe Bounds Checking

When to Use

  • Working with array access or buffer operations
  • Implementing cursor positioning logic (text editors, terminal emulators)
  • Handling viewport rendering and scrolling
  • Dealing with 0-based indices vs 1-based lengths
  • Validating range boundaries
  • Converting VT-100 ranges to Rust ranges
  • Before creating commits with bounds-sensitive code
  • When user asks about "bounds checking", "type safety", "off-by-one errors", etc.

The Problem

Raw usize values are ambiguous and error-prone:

// ❌ Bad - What is `x`? Index or length?
let x = 10_usize;
if x < length {  // Off-by-one error waiting to happen
    buffer[x]
}

// Is this an index (0-based) or a length (1-based)?
// The type system can't help us!

The Solution

Use type-safe wrappers from tui/src/core/units/bounds_check/:

// ✅ Good - Types make it clear
use r3bl_tui::{idx, len, ArrayBoundsCheck};

let index = idx(10);      // Clearly an index (0-based)
let length = len(100);    // Clearly a length (1-based)

if index.overflows(length) {
    // Safely caught! Can't accidentally compare incompatible types
}

Core Principles

Follow these principles when working with indices and lengths:

  1. Use Index types (0-based) instead of usize

    • RowIndex, ColIndex, Index
    • Construct with row(), col(), idx()
  2. Use Length types (1-based) instead of usize

    • RowHeight, ColWidth, Length
    • Construct with height(), width(), len()
  3. Type-safe comparisons

    • Cannot compare RowIndex with ColWidth (compile error!)
    • Prevents category errors like "is row 5 < width 10?"
  4. Use .is_zero() for zero checks

    • Instead of == 0
    • More idiomatic with newtype wrappers
  5. Distinguish navigation from measurement

    • Navigation (index - offset → index): Moving backward in position space
    • Measurement (index.distance_from(other) → length): Calculating distance between positions
    • Use - for cursor movement, use distance_from() for calculating spans

Common Imports

use std::ops::Range;
use r3bl_tui::{
    // Traits
    ArrayBoundsCheck, CursorBoundsCheck, ViewportBoundsCheck,
    RangeBoundsExt, RangeConvertExt, RangeExt, IndexOps, LengthOps,

    // Status enums
    ArrayOverflowResult, CursorPositionBoundsStatus,
    RangeValidityStatus, RangeBoundsResult,

    // Type constructors
    col, row, width, height, idx, len,

    // Terminal delta types (relative cursor movement)
    TermRowDelta, TermColDelta, term_row_delta, term_col_delta,
};

Quick Pattern Reference

Use CaseTraitKey MethodWhen to Use
Array accessArrayBoundsCheckindex.overflows(length)Validating buffer[index] access (index < length)
Cursor positioningCursorBoundsChecklength.check_cursor_position_bounds(pos)Text editing where cursor can be at end (index <= length)
Viewport visibilityViewportBoundsCheckindex.check_viewport_bounds(start, size)Rendering optimization (is content on-screen?)
Range validationRangeBoundsExtrange.check_range_is_valid_for_length(len)Iterator bounds, algorithm parameters
Range membershipRangeBoundsExtrange.check_index_is_within(index)VT-100 scroll regions, text selections
Range conversionRangeConvertExtinclusive_range.to_exclusive()Converting VT-100 ranges for Rust iteration
Slice indexingRangeExtrange.as_usize_range()Converting strongly-typed index ranges into raw [usize] ranges for slice indexing
Relative movementTermRowDelta/TermColDeltaTermRowDelta::new(n) returns OptionANSI cursor movement preventing CSI zero bug

Detailed Examples

Example 1: Array Bounds Checking

Use ArrayBoundsCheck when validating buffer access.

use r3bl_tui::{idx, len, ArrayBoundsCheck, ArrayOverflowResult};

let buffer_length = len(100);
let index = idx(50);

match index.overflows(buffer_length) {
    ArrayOverflowResult::Within => {
        // Safe to access: buffer[50]
        let value = buffer[index.value()];
    }
    ArrayOverflowResult::Overflows => {
        // Out of bounds! Handle error
        eprintln!("Index {} overflows buffer length {}", index, buffer_length);
    }
}

Mathematical law:

  • For valid access: 0 <= index < length
  • Or equivalently: index < length (since Index is always >= 0)

Example 2: Cursor Position Bounds

Use CursorBoundsCheck for text cursor positioning.

use r3bl_tui::{idx, len, CursorBoundsCheck, CursorPositionBoundsStatus};

let text_length = len(10);  // Text has 10 characters
let cursor = idx(10);        // Cursor at position 10 (after last char)

match text_length.check_cursor_position_bounds(cursor) {
    CursorPositionBoundsStatus::Within => {
        // Valid! Cursor CAN be at position 10 (after char 9)
        // User can insert text here
    }
    CursorPositionBoundsStatus::Overflows => {
        // Invalid cursor position
    }
}

Mathematical law:

  • For valid cursor: 0 <= position <= length
  • Note: Cursor CAN be at length (after the last character)

Key difference from array access:

  • Array access: index < length (strict inequality)
  • Cursor position: index <= length (includes equality)

Example 3: Viewport Visibility Check

Use ViewportBoundsCheck to optimize rendering.

use r3bl_tui::{idx, len, ViewportBoundsCheck};

let line_index = idx(150);      // Line 150 in document
let viewport_start = idx(100);  // Viewport starts at line 100
let viewport_size = len(50);    // Viewport shows 50 lines

if line_index.check_viewport_bounds(viewport_start, viewport_size) {
    // Line 150 is visible (100 <= 150 < 150)
    // Render this line
} else {
    // Line is off-screen, skip rendering
}

Mathematical law:

  • Visible if: viewport_start <= index < viewport_start + viewport_size

Example 4: Range Validation

Use RangeBoundsExt to validate range boundaries.

use r3bl_tui::{len, RangeBoundsExt, RangeValidityStatus};

let buffer_length = len(100);
let range = 10..50;  // Want to process elements 10-49

match range.check_range_is_valid_for_length(buffer_length) {
    RangeValidityStatus::Valid => {
        // Range is valid for this buffer
        for i in range {
            process(buffer[i]);
        }
    }
    RangeValidityStatus::Invalid(reason) => {
        eprintln!("Invalid range: {}", reason);
    }
}

Example 5: Range Membership

Use RangeBoundsExt to check if index is within a range.

use r3bl_tui::{idx, RangeBoundsExt};

// VT-100 scroll region: lines 5-15
let scroll_region = 5..=15;  // Inclusive range
let cursor_row = idx(10);

if scroll_region.check_index_is_within(cursor_row) {
    // Cursor is within scroll region
    // Apply scroll behavior
} else {
    // Cursor outside scroll region
}

Example 6: Range Conversion

Use RangeConvertExt to convert inclusive to exclusive ranges.

use r3bl_tui::RangeConvertExt;

// VT-100 uses inclusive ranges: 1..=10 means lines 1 through 10
let vt100_range = 1..=10;

// Rust iterators use exclusive ranges: 1..11
let rust_range = vt100_range.to_exclusive();

// Now can use in Rust iteration
for line in rust_range {
    process_line(line);
}

Example 7: Navigation vs Measurement

Use - for navigation (moving cursor), distance_from() for measurement (calculating spans).

use r3bl_tui::{row, height, RowIndex, RowHeight};

// Navigation: Move cursor backward by offset (returns RowIndex).
let cursor_pos = row(10);
let new_pos = cursor_pos - row(3);  // row(7) - moved 3 positions back
// Uses saturating subtraction: row(2) - row(5) = row(0), not overflow

// Measurement: Calculate distance between two positions (returns RowHeight).
let start = row(5);
let end = row(15);
let distance: RowHeight = end.distance_from(start);  // height(10) - 10 rows apart
// Panics if start > end (negative distance)

When to use which:

  • Moving cursor up/down/left/right → - operator
  • Calculating scroll amount, viewport span, selection size → distance_from()

Example 8: Terminal Cursor Movement (Make Illegal States Unrepresentable)

Use TermRowDelta/TermColDelta for relative cursor movement in ANSI sequences.

The CSI zero problem: ANSI cursor movement commands interpret parameter 0 as 1:

  • CSI 0 A (CursorUp with n=0) moves cursor 1 row up, not 0
  • CSI 0 C (CursorForward with n=0) moves cursor 1 column right, not 0

Solution: TermRowDelta and TermColDelta wrap NonZeroU16 internally, making zero-valued deltas impossible to represent. Construction is fallible:

use r3bl_tui::{TermRowDelta, TermColDelta, CsiSequence};
use std::io::Write;

// Calculate cursor movement from position on 80-column terminal.
let position: u16 = 240;  // 240 chars from start
let term_width: u16 = 80;

// Fallible construction - must handle the None case.
// For position 240: rows = 3 (Some), cols = 0 (None).
if let Some(delta) = TermRowDelta::new(position / term_width) {
    // delta is gua

---

*Content truncated.*

When not to use it

  • Performance-critical code where zero-cost abstractions are prohibited

Limitations

  • Requires adopting specific type-safe wrappers

How it compares

It replaces ambiguous raw integers with domain-specific types to catch off-by-one errors at compile time.

Compared to similar skills

check-bounds-safety side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
check-bounds-safety (this skill)12moNo flagsIntermediate
reproduce-reduce-regress17moNo flagsIntermediate
review-and-fix01moNo flagsAdvanced
memory-safety-patterns44moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry