Struct Literals with Shorthand in Home
On this page 41
Struct literals in Home support multiple convenient syntaxes including field shorthand, making struct initialization concise and readable.
Table of Contents
- Basic Syntax
- Shorthand Syntax
- Struct Update Syntax
- Anonymous Structs
- Tuple Structs
- Nested Structs
- Best Practices
Basic Syntax
Explicit Field Initialization
struct User {
name: string,
age: i32,
email: string,
}
// Explicit initialization
let user = User {
name: "Alice",
age: 30,
email: "alice@example.com",
}
Field Order
Fields can be initialized in any order:
let user = User {
email: "bob@example.com",
name: "Bob",
age: 25,
}
Shorthand Syntax
Field Punning
When a variable name matches the field name, use shorthand:
let name = "Charlie"
let age = 35
let email = "charlie@example.com"
// Shorthand syntax
let user = User { name, age, email }
// Equivalent to:
let user = User {
name: name,
age: age,
email: email,
}
Mixed Shorthand and Explicit
let name = "Dave"
let email = "dave@example.com"
// Mix shorthand and explicit
let user = User {
name,
age: 28,
email,
}
Benefits
- Less repetition - DRY principle
- Cleaner code - Easier to read
- Refactoring friendly - Rename once, works everywhere
// Before shorthand
fn create_user(name: string, age: i32, email: string): User {
User {
name: name,
age: age,
email: email,
}
}
// With shorthand
fn create_user(name: string, age: i32, email: string): User {
User { name, age, email }
}
Struct Update Syntax
Spread Operator
Use .. to copy fields from another struct:
let user1 = User {
name: "Alice",
age: 30,
email: "alice@example.com",
}
// Create new struct with some fields updated
let user2 = User {
age: 31,
..user1 // Copy other fields from user1
}
// user2 = { name: "Alice", age: 31, email: "alice@example.com" }
Update Multiple Fields
let user3 = User {
name: "Alice Smith",
email: "alice.smith@example.com",
..user1 // age: 30
}
Shorthand with Update
let name = "Bob"
let user = User {
name,
..default_user
}
Anonymous Structs
Inline Struct Types
Create structs without defining a type:
// Anonymous struct literal
let point = .{ x: 10, y: 20 }
// Type is inferred
fn draw_at(pos: .{ x: i32, y: i32 }): void {
println("Drawing at ({}, {})", pos.x, pos.y)
}
draw_at(.{ x: 100, y: 200 })
Return Anonymous Structs
fn get_bounds(): .{ min: i32, max: i32 } {
.{ min: 0, max: 100 }
}
let bounds = get_bounds()
println("Range: {} to {}", bounds.min, bounds.max)
Shorthand in Anonymous Structs
let x = 10
let y = 20
let point = .{ x, y } // Shorthand works here too
Tuple Structs
Positional Fields
Tuple structs use positional initialization:
struct Point(i32, i32)
struct Color(u8, u8, u8)
// Tuple struct literals
let point = Point(10, 20)
let color = Color(255, 128, 0)
// Access by position
println("Point: ({}, {})", point.0, point.1)
println("Color: RGB({}, {}, {})", color.0, color.1, color.2)
Named Tuple Structs
struct Rgb(r: u8, g: u8, b: u8)
// Can use named or positional
let color1 = Rgb(255, 128, 0)
let color2 = Rgb(r: 255, g: 128, b: 0)
Nested Structs
Nested Initialization
struct Address {
street: string,
city: string,
zip: string,
}
struct Person {
name: string,
age: i32,
address: Address,
}
// Nested struct literal
let person = Person {
name: "Alice",
age: 30,
address: Address {
street: "123 Main St",
city: "Springfield",
zip: "12345",
},
}
Shorthand in Nested Structs
let name = "Bob"
let age = 25
let street = "456 Oak Ave"
let city = "Portland"
let zip = "97201"
let person = Person {
name,
age,
address: Address { street, city, zip },
}
Update Nested Structs
let person2 = Person {
name: "Charlie",
address: Address {
street: "789 Pine Rd",
..person.address // Copy city and zip
},
..person // Copy age
}
Best Practices
1. Use Shorthand When Possible
// Good - concise
fn create_user(name: string, age: i32, email: string): User {
User { name, age, email }
}
// Avoid - repetitive
fn create_user(name: string, age: i32, email: string): User {
User {
name: name,
age: age,
email: email,
}
}
2. Consistent Field Order
// Good - consistent with struct definition
struct User {
name: string,
age: i32,
email: string,
}
let user = User {
name: "Alice",
age: 30,
email: "alice@example.com",
}
// Works but less readable
let user = User {
email: "alice@example.com",
name: "Alice",
age: 30,
}
3. Use Update Syntax for Defaults
const DEFAULT_CONFIG: Config = Config {
host: "localhost",
port: 8080,
timeout: 30,
debug: false,
}
// Override specific fields
let dev_config = Config {
port: 3000,
debug: true,
..DEFAULT_CONFIG
}
4. Anonymous Structs for One-Off Data
// Good - simple return value
fn get_stats(): .{ count: i32, average: f64 } {
.{ count: 100, average: 75.5 }
}
// Avoid - defining struct for single use
struct Stats {
count: i32,
average: f64,
}
fn get_stats(): Stats {
Stats { count: 100, average: 75.5 }
}
5. Multiline for Readability
// Good - readable
let config = ServerConfig {
host: "api.example.com",
port: 443,
ssl: true,
timeout: 60,
max_connections: 1000,
workers: 8,
}
// Avoid - hard to read
let config = ServerConfig { host: "api.example.com", port: 443, ssl: true, timeout: 60, max_connections: 1000, workers: 8 }
Common Patterns
Builder Pattern Alternative
// Instead of builder pattern
let user = User {
name: "Alice",
age: 30,
..User::default()
}
Configuration Objects
struct HttpConfig {
timeout: i32,
retries: i32,
verify_ssl: bool,
}
const DEFAULT_HTTP: HttpConfig = HttpConfig {
timeout: 30,
retries: 3,
verify_ssl: true,
}
// Easy customization
let custom = HttpConfig {
timeout: 60,
..DEFAULT_HTTP
}
Test Data
fn test_user_creation() {
let name = "Test User"
let email = "test@example.com"
let user = User {
name,
age: 25,
email,
active: true,
}
assert_eq!(user.name, "Test User")
}
Response Objects
fn handle_request(req: Request): Response {
let status = 200
let body = "OK"
Response {
status,
body,
headers: HashMap::new(),
..Response::default()
}
}
Examples
Point and Rectangle
struct Point {
x: i32,
y: i32,
}
struct Rect {
top_left: Point,
bottom_right: Point,
}
let x1 = 10
let y1 = 20
let x2 = 100
let y2 = 80
let rect = Rect {
top_left: Point { x: x1, y: y1 },
bottom_right: Point { x: x2, y: y2 },
}
// With shorthand
let rect2 = Rect {
top_left: Point { x: x1, y: y1 },
bottom_right: Point { x: x2, y: y2 },
}
User Profile
struct Profile {
username: string,
display_name: string,
bio: string,
avatar_url: string,
verified: bool,
}
fn create_profile(username: string, display_name: string): Profile {
Profile {
username,
display_name,
bio: "",
avatar_url: "/default-avatar.png",
verified: false,
}
}
// Update profile
fn verify_profile(profile: Profile): Profile {
Profile {
verified: true,
..profile
}
}
Database Record
struct DbRecord {
id: i64,
created_at: DateTime,
updated_at: DateTime,
data: string,
}
fn create_record(data: string): DbRecord {
let now = DateTime::now()
DbRecord {
id: generate_id(),
created_at: now,
updated_at: now,
data,
}
}
fn update_record(record: DbRecord, data: string): DbRecord {
DbRecord {
data,
updated_at: DateTime::now(),
..record
}
}
Color Manipulation
struct Color {
r: u8,
g: u8,
b: u8,
a: u8,
}
const RED: Color = Color { r: 255, g: 0, b: 0, a: 255 }
const GREEN: Color = Color { r: 0, g: 255, b: 0, a: 255 }
const BLUE: Color = Color { r: 0, g: 0, b: 255, a: 255 }
fn with_alpha(color: Color, alpha: u8): Color {
Color {
a: alpha,
..color
}
}
let semi_transparent_red = with_alpha(RED, 128)
Event Data
struct ClickEvent {
x: i32,
y: i32,
button: MouseButton,
modifiers: KeyModifiers,
timestamp: i64,
}
fn handle_click(x: i32, y: i32, button: MouseButton): void {
let event = ClickEvent {
x,
y,
button,
modifiers: get_modifiers(),
timestamp: current_time(),
}
process_event(event)
}
See Also
- Structs - Struct definitions
- Pattern Matching - Destructuring structs
- Methods - Struct methods