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.zipInstalls 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.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
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:
-
Use Index types (0-based) instead of
usizeRowIndex,ColIndex,Index- Construct with
row(),col(),idx()
-
Use Length types (1-based) instead of
usizeRowHeight,ColWidth,Length- Construct with
height(),width(),len()
-
Type-safe comparisons
- Cannot compare
RowIndexwithColWidth(compile error!) - Prevents category errors like "is row 5 < width 10?"
- Cannot compare
-
Use
.is_zero()for zero checks- Instead of
== 0 - More idiomatic with newtype wrappers
- Instead of
-
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, usedistance_from()for calculating spans
- Navigation (
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 Case | Trait | Key Method | When to Use |
|---|---|---|---|
| Array access | ArrayBoundsCheck | index.overflows(length) | Validating buffer[index] access (index < length) |
| Cursor positioning | CursorBoundsCheck | length.check_cursor_position_bounds(pos) | Text editing where cursor can be at end (index <= length) |
| Viewport visibility | ViewportBoundsCheck | index.check_viewport_bounds(start, size) | Rendering optimization (is content on-screen?) |
| Range validation | RangeBoundsExt | range.check_range_is_valid_for_length(len) | Iterator bounds, algorithm parameters |
| Range membership | RangeBoundsExt | range.check_index_is_within(index) | VT-100 scroll regions, text selections |
| Range conversion | RangeConvertExt | inclusive_range.to_exclusive() | Converting VT-100 ranges for Rust iteration |
| Slice indexing | RangeExt | range.as_usize_range() | Converting strongly-typed index ranges into raw [usize] ranges for slice indexing |
| Relative movement | TermRowDelta/TermColDelta | TermRowDelta::new(n) returns Option | ANSI 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(CursorUpwith n=0) moves cursor 1 row up, not 0CSI 0 C(CursorForwardwith 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.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| check-bounds-safety (this skill) | 1 | 2mo | No flags | Intermediate |
| reproduce-reduce-regress | 1 | 7mo | No flags | Intermediate |
| review-and-fix | 0 | 1mo | No flags | Advanced |
| memory-safety-patterns | 4 | 4mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by r3bl-org
View all by r3bl-org →You might also like
reproduce-reduce-regress
facet-rs
Systematic workflow for debugging by reproducing bugs with real data, reducing test cases to minimal examples, and adding regression tests
review-and-fix
robustmq
Deep analysis and iterative fixing of a Rust source file. Finds logic errors, lock/concurrency issues, and simplification opportunities, then fixes them one by one until the file is clean.
memory-safety-patterns
sickn33
Implement memory-safe programming with RAII, ownership, smart pointers, and resource management across Rust, C++, and C. Use when writing safe systems code, managing resources, or preventing memory bugs.
debug-cli
antinomyhq
Use when users need to debug, modify, or extend the code-forge application's CLI commands, argument parsing, or CLI behavior. This includes adding new commands, fixing CLI bugs, updating command options, or troubleshooting CLI-related issues.
fix-clippy
quickwit-oss
Fix all clippy lint warnings in the project
debug-lldb
regenrek
Capture and analyze thread backtraces with LLDB/GDB to debug hangs, deadlocks, UI freezes, IPC stalls, or high-CPU loops across any language or project. Use when an app becomes unresponsive, switching contexts stalls, or you need thread stacks to locate lock inversion or blocking calls.