Slices
Slices are a fundamental operator in Harneet that allow you to extract portions of arrays, strings, and typed arrays using the [start:end] syntax. This Go-like slicing syntax is consistent across all sequence types.
Overview
Slicing in Harneet: - Syntax: sequence[start:end], sequence[:end], sequence[start:], sequence[:] - Universal: Works on arrays, typed arrays, and strings - Half-open interval: start is inclusive, end is exclusive - Safe: Automatically clamps indices to valid bounds - Type-preserving: Typed arrays preserve their element type
Slice Syntax
Harneet supports four forms of slice notation:
| Syntax | Description | Example |
seq[a:b] | From index a to b (exclusive) | arr[1:4] → elements at indices 1, 2, 3 |
seq[:b] | From start to b (exclusive) | arr[:3] → first 3 elements |
seq[a:] | From index a to end | arr[2:] → from index 2 onwards |
seq[:] | Full copy | arr[:] → complete shallow copy |
Array Slicing
Slicing arrays creates new arrays with the specified elements:
| Array Slicing |
|---|
| package main
import fmt
var arr = [10, 20, 30, 40, 50]
// Basic slicing
var mid = arr[1:4] // [20, 30, 40]
var first = arr[:3] // [10, 20, 30]
var last = arr[2:] // [30, 40, 50]
var copy = arr[:] // [10, 20, 30, 40, 50]
// Empty slices
var empty1 = arr[2:2] // []
var empty2 = arr[5:5] // []
// Out of bounds - automatically clamped
var clamped1 = arr[-5:2] // Treated as arr[0:2] → [10, 20]
var clamped2 = arr[3:99] // Treated as arr[3:5] → [40, 50]
// Start > end = empty
var empty3 = arr[4:2] // []
fmt.Println("Mid:", mid)
fmt.Println("First:", first)
fmt.Println("Last:", last)
|
String Slicing
String slicing extracts substrings (currently byte-based):
| String Slicing |
|---|
| package main
import fmt
var text = "Hello, World!"
// Basic slicing
var hello = text[0:5] // "Hello"
var world = text[7:12] // "World"
var greeting = text[:5] // "Hello"
var rest = text[7:] // "World!"
var copy = text[:] // "Hello, World!"
// Empty slices
var empty = text[5:5] // ""
fmt.Println("Hello:", hello)
fmt.Println("World:", world)
|
Note: String slicing is currently byte-based. For multi-byte UTF-8 characters, ensure indices align with character boundaries to avoid splitting characters.
| UTF-8 Considerations |
|---|
| package main
import fmt
var text = "Hello 世界"
// Be careful with multi-byte characters
// "世" is 3 bytes in UTF-8
var substr = text[6:9] // First Chinese character (bytes 6-8)
fmt.Println(substr) // "世"
// Safer: use string functions for complex Unicode handling
|
Typed Array Slicing
Typed arrays preserve their element type when sliced:
| Typed Array Slicing |
|---|
| package main
import fmt
var numbers int[5] = [1, 2, 3, 4, 5]
// Slicing preserves type
var slice1 = numbers[1:4] // int[] with [2, 3, 4]
var slice2 = numbers[:2] // int[] with [1, 2]
// Type safety maintained
fmt.Println(slice1) // [2, 3, 4]
// Can only assign compatible typed arrays
var moreNumbers int[] = [6, 7, 8]
var combined = slice1.concat(moreNumbers) // Works - same type
|
Slicing Rules
1. Index Bounds
Indices are automatically clamped to valid ranges:
| Index Clamping |
|---|
| package main
var arr = [1, 2, 3, 4, 5]
// Negative indices clamped to 0
var slice1 = arr[-10:3] // Same as arr[0:3] → [1, 2, 3]
// Indices beyond length clamped to len
var slice2 = arr[2:100] // Same as arr[2:5] → [3, 4, 5]
// Both clamped
var slice3 = arr[-5:10] // Same as arr[0:5] → [1, 2, 3, 4, 5]
|
2. Empty Slices
When start >= end, the result is an empty slice:
| Empty Slices |
|---|
| package main
var arr = [1, 2, 3, 4, 5]
var empty1 = arr[3:3] // []
var empty2 = arr[5:5] // []
var empty3 = arr[4:2] // start > end → []
var empty4 = arr[10:15] // Both beyond bounds → []
|
3. Half-Open Interval
The slice is half-open: start is inclusive, end is exclusive:
| Half-Open Interval |
|---|
| package main
var arr = [0, 1, 2, 3, 4]
var slice = arr[1:3] // Indices 1 and 2 (not 3) → [1, 2]
|
4. Shallow Copy
Slicing creates a new collection with copied elements (shallow copy):
| Shallow Copy |
|---|
| package main
var original = [1, 2, 3, 4, 5]
var copy = original[:]
// Modifying copy doesn't affect original
copy.push(6)
fmt.Println("Original:", original) // [1, 2, 3, 4, 5]
fmt.Println("Copy:", copy) // [1, 2, 3, 4, 5, 6]
|
Use Cases
| Extract Elements |
|---|
| package main
var scores = [95, 87, 92, 88, 91]
// Top 3 scores (assuming sorted descending)
var top3 = scores[:3] // [95, 87, 92]
// Last 2 scores
var last2 = scores[3:] // [88, 91]
// Middle scores
var middle = scores[1:4] // [87, 92, 88]
|
| Pagination |
|---|
| package main
var items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var pageSize = 3
var pageNum = 1 // 0-indexed
var start = pageNum * pageSize
var end = start + pageSize
var page = items[start:end] // [4, 5, 6]
|
String Manipulation
| String Operations |
|---|
| package main
var filename = "document.txt"
// Extract extension
var dotIndex = filename.lastIndexOf(".")
var extension = filename[dotIndex:] // ".txt"
// Extract name without extension
var name = filename[:dotIndex] // "document"
// First N characters
var prefix = filename[:3] // "doc"
|
Removing Elements
| Remove by Slicing |
|---|
| package main
var arr = [1, 2, 3, 4, 5]
// Remove element at index 2 (value 3)
var before = arr[:2] // [1, 2]
var after = arr[3:] // [4, 5]
var result = before.concat(after) // [1, 2, 4, 5]
|
Complete Example
| Complete Slicing Example |
|---|
| package main
import fmt
// Array slicing
var numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
fmt.Println("Original:", numbers)
fmt.Println("First half:", numbers[:5])
fmt.Println("Second half:", numbers[5:])
fmt.Println("Middle:", numbers[3:7])
fmt.Println("Every other (manual):", [numbers[0], numbers[2], numbers[4]])
// String slicing
var message = "Hello, Harneet Programming Language!"
fmt.Println("\nString:", message)
fmt.Println("Greeting:", message[:5])
fmt.Println("Language:", message[23:])
fmt.Println("Without punctuation:", message[:len(message)-1])
// Typed array slicing
var scores int[] = [95, 87, 92, 88, 91, 85, 94]
var topScores = scores[:3]
var avgScores = scores[3:5]
var lowScores = scores[5:]
fmt.Println("\nTop scores:", topScores)
fmt.Println("Average scores:", avgScores)
fmt.Println("Low scores:", lowScores)
// Copy and modify
var scoresCopy = scores[:]
scoresCopy.push(100)
fmt.Println("\nOriginal unchanged:", scores)
fmt.Println("Copy with new score:", scoresCopy)
|
Comparison with Other Languages
| Language | Syntax | Notes |
| Harneet | seq[a:b], seq[:b], seq[a:], seq[:] | Go-like, auto-clamps bounds |
| Go | seq[a:b], seq[:b], seq[a:], seq[:] | Panics on out-of-bounds |
| Python | seq[a:b], seq[:b], seq[a:], seq[:] | Supports negative indices |
| JavaScript | seq.slice(a, b) | Method, not operator |
| Rust | &seq[a..b], &seq[..b], &seq[a..] | Creates references |
Best Practices
1. Use Slice Operator for Clarity
| Clear Intent |
|---|
| // Preferred: Clear slice syntax
var first10 = items[:10]
// Less clear: Manual loop
var first10 = []
for i = 0; i < 10; i = i + 1 {
first10.push(items[i])
}
|
2. Prefer Slicing Over Index Loops
| Prefer Slicing |
|---|
| // Good: Simple and clear
var subset = arr[5:10]
// Avoid: Unnecessary complexity
var subset = []
for i = 5; i < 10; i = i + 1 {
subset.push(arr[i])
}
|
3. Be Mindful of UTF-8 with Strings
| UTF-8 Safety |
|---|
| package main
import strings
// For ASCII strings: slice freely
var ascii = "Hello"
var part = ascii[0:3] // "Hel"
// For Unicode: be careful or use string functions
var unicode = "你好世界"
// Prefer: use string methods for complex operations
var chars = strings.Split(unicode, "")
|
See Also