Structs
Structs in Y allow you to create custom data types by grouping related fields together. They're fundamental for organizing data and creating meaningful abstractions in your programs.
Struct Declaration
Define a struct using the struct
keyword followed by field declarations:
struct Person {
name: str;
age: i64;
}
struct Point {
x: f64;
y: f64;
}
struct TestStruct {
x: i64;
bar: (i64, i64) -> i64; // Function type as field
}
Each field has:
- A name (identifier)
- A type annotation
- A semicolon terminator
Struct Instantiation
Create struct instances using struct literal syntax:
let person = Person {
name: "Alice",
age: 30
};
let origin = Point {
x: 0.0,
y: 0.0
};
// Using function references
let my_struct = TestStruct {
x: 42,
bar: add // add is a function
};
All fields must be provided during instantiation.
Field Access
Access struct fields using dot notation:
let person = Person {
name: "Bob",
age: 25
};
let name = person.name; // "Bob"
let age = person.age; // 25
Mutable Structs
Structs can be mutable, allowing field modification:
let mut person = Person {
name: "Charlie",
age: 20
};
person.age = 21; // Modify age field
person.name = "Chuck"; // Modify name field
Nested Structs
Structs can contain other structs as fields:
struct Address {
street: str;
city: str;
}
struct Person {
name: str;
address: Address;
}
let person = Person {
name: "David",
address: Address {
street: "123 Main St",
city: "Springfield"
}
};
// Access nested fields
let city = person.address.city;
Functions as Struct Fields
Y allows function types as struct fields:
struct Calculator {
operation: (i64, i64) -> i64;
name: str;
}
fn add(x: i64, y: i64): i64 {
x + y
}
let calc = Calculator {
operation: add,
name: "Adder"
};
// Call the function through the struct
let result = calc.operation(10, 20); // 30
Real Examples from Y Code
Complex Struct Usage
struct FooStruct {
id: i64;
amount: f64;
}
struct Bar {
t: TestStruct;
}
fn main(): void {
let foo = FooStruct {
id: 42,
amount: 133.7
};
let mut b = Bar {
t: TestStruct {
x: 1337,
bar: add
}
};
// Nested field access and modification
b.t.x = 42;
// Calling function through nested struct
b.t.bar(4, 2);
}
Assignment Example
struct Foo {
b: i64;
}
fn b(): i64 {
17
}
fn main(): void {
let mut a = Foo {
b: b() // Function call in field initialization
};
a.b = 42; // Direct field assignment
}
Methods on Structs
Structs can have methods defined through instance blocks:
struct FooStruct {
id: i64;
amount: f64;
}
instance FooStruct {
fn get_id(): i64 {
this.id
}
fn get_amount(): f64 {
this.amount
}
fn set_amount(new_amount: f64): void {
this.amount = new_amount;
}
}
// Usage
let foo = FooStruct {
id: 42,
amount: 133.7
};
let id = foo.get_id(); // Method call
let amount = foo.get_amount(); // Another method call
Struct Patterns and Best Practices
Grouping Related Data
// Good: Related fields grouped together
struct Circle {
center_x: f64;
center_y: f64;
radius: f64;
}
// Better: Using nested structs for better organization
struct Point {
x: f64;
y: f64;
}
struct Circle {
center: Point;
radius: f64;
}
Using Structs as Parameters
fn calculate_area(circle: Circle): f64 {
const PI: f64 = 3.14159;
return PI * circle.radius * circle.radius;
}
fn move_point(point: Point, dx: f64, dy: f64): Point {
Point {
x: point.x + dx,
y: point.y + dy
}
}
Structs with Arrays
struct Student {
name: str;
grades: &[i64];
}
struct Class {
name: str;
students: &[Student];
}
let math_class = Class {
name: "Mathematics",
students: &[
Student {
name: "Alice",
grades: &[95, 87, 92]
},
Student {
name: "Bob",
grades: &[88, 79, 94]
}
]
};
Common Patterns
Builder Pattern
struct Config {
debug: bool;
port: i64;
host: str;
}
fn create_config(): Config {
Config {
debug: false,
port: 8080,
host: "localhost"
}
}
Data Transfer Objects
struct UserData {
username: str;
email: str;
created_at: i64; // timestamp
}
struct Response {
status: i64;
data: UserData;
message: str;
}
Memory and Performance
- Structs are value types - they contain the actual data
- Field access is direct and efficient
- Nested structs are stored inline
- Mutable structs allow in-place modification
Best Practices
- Use descriptive names: Choose clear field and struct names
- Group related data: Keep related fields together in the same struct
- Consider immutability: Use mutable structs only when necessary
- Organize with nesting: Use nested structs for better data organization
- Document complex structures: Use comments for complex struct relationships
// Good struct design
struct BankAccount {
account_number: str;
balance: f64;
owner: Person; // Nested struct
is_active: bool;
}
// Clear field access
let account = create_account();
let balance = account.balance;
let owner_name = account.owner.name;