Splat Operators in Home
On this page 44
Splat operators (also known as spread/rest operators) provide powerful syntax for unpacking and collecting elements in arrays, objects, and function calls.
Table of Contents
- Array Spread
- Object Spread
- Function Call Spread
- Rest Parameters
- Array Destructuring
- Object Destructuring
- Best Practices
Array Spread
Spreading Arrays
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
// Spread arrays into new array
let combined = [...arr1, ...arr2]
// Result: [1, 2, 3, 4, 5, 6]
// Mix spread with literals
let extended = [0, ...arr1, 10, ...arr2, 20]
// Result: [0, 1, 2, 3, 10, 4, 5, 6, 20]
Copying Arrays
let original = [1, 2, 3, 4, 5]
// Shallow copy
let copy = [...original]
// Modify copy without affecting original
copy.push(6)
// original: [1, 2, 3, 4, 5]
// copy: [1, 2, 3, 4, 5, 6]
Concatenation
let fruits = ["apple", "banana"]
let vegetables = ["carrot", "potato"]
let dairy = ["milk", "cheese"]
// Concatenate multiple arrays
let groceries = [...fruits, ...vegetables, ...dairy]
Object Spread
Spreading Objects
let defaults = {
host: "localhost",
port: 8080,
timeout: 30,
}
let custom = {
port: 3000,
debug: true,
}
// Merge objects (later values override)
let config = { ...defaults, ...custom }
// Result: { host: "localhost", port: 3000, timeout: 30, debug: true }
Copying Objects
let original = {
name: "Alice",
age: 30,
email: "alice@example.com",
}
// Shallow copy
let copy = { ...original }
// Override specific fields
let updated = {
...original,
age: 31,
active: true,
}
Conditional Spreading
let base_config = { host: "localhost", port: 8080 }
// Conditionally add fields
let config = {
...base_config,
...(is_production ? { ssl: true, verify: true } : {}),
debug: !is_production,
}
Function Call Spread
Spreading Arguments
fn add(a: i32, b: i32, c: i32): i32 {
a + b + c
}
let numbers = [1, 2, 3]
// Spread array as arguments
let sum = add(...numbers)
// Equivalent to: add(1, 2, 3)
Mix Spread with Regular Arguments
fn greet(greeting: string, ...names: string): void {
for name in names {
println("{}, {}!", greeting, name)
}
}
let people = ["Alice", "Bob"]
// Mix regular and spread arguments
greet("Hello", ...people, "Charlie")
// Equivalent to: greet("Hello", "Alice", "Bob", "Charlie")
Multiple Spreads
fn sum(...numbers: i32): i32 {
numbers.iter().sum()
}
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
// Multiple spreads in one call
let total = sum(...arr1, ...arr2, 7, 8, 9)
// Equivalent to: sum(1, 2, 3, 4, 5, 6, 7, 8, 9)
Rest Parameters
Collecting Arguments
// Rest parameter collects remaining arguments
fn sum(first: i32, ...rest: i32): i32 {
let total = first
for num in rest {
total += num
}
total
}
sum(1, 2, 3, 4, 5) // first=1, rest=[2,3,4,5]
With Named Parameters
fn log(level: string, message: string, ...details: any): void {
println("[{}] {}", level, message)
for detail in details {
println(" {:?}", detail)
}
}
log("INFO", "User logged in", user_id, timestamp, ip_address)
Array Destructuring
Basic Destructuring with Rest
let numbers = [1, 2, 3, 4, 5]
// Destructure with rest
let [first, second, ...rest] = numbers
// first: 1, second: 2, rest: [3, 4, 5]
// Rest in middle (if supported)
let [head, ...middle, tail] = numbers
// head: 1, middle: [2, 3, 4], tail: 5
Ignoring Elements
let data = [1, 2, 3, 4, 5]
// Ignore some elements
let [first, _, third, ...rest] = data
// first: 1, third: 3, rest: [4, 5]
// Only get rest
let [_, _, ...rest] = data
// rest: [3, 4, 5]
Nested Destructuring
let matrix = [[1, 2], [3, 4], [5, 6]]
// Destructure nested arrays
let [[a, b], ...rest] = matrix
// a: 1, b: 2, rest: [[3, 4], [5, 6]]
Swapping Values
let a = 1
let b = 2
// Swap using destructuring
[a, b] = [b, a]
// a: 2, b: 1
Object Destructuring
Basic Destructuring with Rest
let user = {
name: "Alice",
age: 30,
email: "alice@example.com",
active: true,
role: "admin",
}
// Destructure with rest
let { name, age, ...rest } = user
// name: "Alice", age: 30, rest: { email: "...", active: true, role: "admin" }
Renaming Fields
let config = {
host: "localhost",
port: 8080,
timeout: 30,
}
// Rename while destructuring
let { host: server, port: serverPort, ...options } = config
// server: "localhost", serverPort: 8080, options: { timeout: 30 }
Default Values
let partial = {
name: "Bob",
age: 25,
}
// Provide defaults for missing fields
let { name, age, email = "unknown@example.com", ...rest } = partial
// name: "Bob", age: 25, email: "unknown@example.com", rest: {}
Nested Destructuring
let person = {
name: "Alice",
address: {
street: "123 Main St",
city: "Portland",
zip: "97201",
},
contacts: {
email: "alice@example.com",
phone: "555-1234",
},
}
// Nested destructuring with rest
let {
name,
address: { city, ...addressRest },
...personRest
} = person
Best Practices
1. Use Spread for Immutability
// Good - creates new array
let new_array = [...original_array, new_item]
// Avoid - mutates original
original_array.push(new_item)
2. Shallow Copy Awareness
let original = {
name: "Alice",
settings: { theme: "dark" },
}
// Shallow copy - nested objects are referenced
let copy = { ...original }
copy.settings.theme = "light" // Affects original!
// Deep copy when needed
let deep_copy = {
...original,
settings: { ...original.settings },
}
3. Rest Parameter Must Be Last
// Good
fn func(a: i32, b: i32, ...rest: i32): void { }
// Error - rest must be last
fn func(a: i32, ...rest: i32, b: i32): void { }
4. Limit Spread Depth
// Good - clear and simple
let config = { ...defaults, ...custom }
// Avoid - too many spreads
let result = { ...a, ...b, ...c, ...d, ...e, ...f }
5. Use Destructuring for Clarity
// Good - clear what's being extracted
let { name, age } = user
// Avoid - accessing repeatedly
println("{}", user.name)
println("{}", user.age)
println("{}", user.name) // Repeated access
Common Patterns
Merging Configurations
const DEFAULT_CONFIG = {
host: "localhost",
port: 8080,
timeout: 30,
retries: 3,
}
fn create_client(config: Config): Client {
let final_config = { ...DEFAULT_CONFIG, ...config }
Client::new(final_config)
}
Removing Properties
let user = {
id: 1,
name: "Alice",
password: "secret",
email: "alice@example.com",
}
// Remove password
let { password, ...safe_user } = user
// safe_user: { id: 1, name: "Alice", email: "..." }
Function Composition
fn compose<T>(...functions: fn(T): T): fn(T): T {
return |x| {
let result = x
for func in functions {
result = func(result)
}
result
}
}
let pipeline = compose(trim, lowercase, remove_spaces)
Partial Application
fn partial<F, Args, Rest>(func: F, ...args: Args): fn(...Rest): Result
where
F: Fn(Args, Rest): Result
{
return |...rest| func(...args, ...rest)
}
let add = |a, b, c| a + b + c
let add_5_and_10 = partial(add, 5, 10)
let result = add_5_and_10(3) // 18
Collecting Middleware
fn apply_middleware(handler: Handler, ...middleware: Middleware): Handler {
let mut wrapped = handler
for mw in middleware.reverse() {
wrapped = mw(wrapped)
}
wrapped
}
let handler = apply_middleware(
base_handler,
...common_middleware,
auth_middleware,
logging_middleware
)
Examples
Array Manipulation
let numbers = [1, 2, 3]
// Prepend
let with_zero = [0, ...numbers]
// Append
let with_four = [...numbers, 4]
// Insert in middle
let with_middle = [...numbers[..2], 99, ...numbers[2..]]
// Remove duplicates
let unique = [...new Set(numbers)]
Object Updates
let user = {
name: "Alice",
age: 30,
email: "alice@example.com",
}
// Update age
let updated = { ...user, age: 31 }
// Add new field
let with_role = { ...user, role: "admin" }
// Update multiple fields
let modified = {
...user,
age: 31,
email: "alice.new@example.com",
active: true,
}
Function Arguments
fn create_user(name: string, age: i32, ...options: UserOption): User {
let mut user = User { name, age }
for option in options {
match option {
UserOption::Email(email) => user.email = email,
UserOption::Role(role) => user.role = role,
UserOption::Active(active) => user.active = active,
}
}
user
}
let user = create_user(
"Alice",
30,
UserOption::Email("alice@example.com"),
UserOption::Role("admin"),
UserOption::Active(true)
)
Matrix Flattening
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
// Flatten using spread
let flat = [...matrix[0], ...matrix[1], ...matrix[2]]
// Or with reduce
let flat = matrix.reduce(|acc, row| [...acc, ...row], [])
See Also
- Destructuring - Pattern matching and destructuring
- Variadic Functions - Variable arguments
- Arrays - Array operations
- Objects - Object manipulation