Structs
Structs in Cyrus are user-defined composite types that group related data together. They are similar to struct in C/C++ but follow Cyrus' type system rules.
Defining a Struct
Use the struct keyword, followed by the name and fields:
struct User {
pub name: char*;
pub age: uint;
king: bool; // private field
}
Fields marked with pub can be accessed from outside the struct. Fields without pub are private and can only be accessed inside the struct or its methods.
Initializing a Struct
Structs can be initialized using struct literals:
var user = User {
name: "Cyrus",
age: 2500,
king: true
};
Accessing Struct Fields
You can access struct fields using the dot operator (.):
printf("%s is %d years old.\n", user.name, user.age);
user.king is not accessible outside the struct because it is private.
Methods
Structs can define functions inside their body, called methods. Methods allow structs to encapsulate behavior along with data.
struct Counter {
pub count: int;
pub fn new(initial: int) Self {
return Self { count: initial };
}
pub fn increment(&self) {
self->count++;
}
pub fn reset(&self) {
self->count = 0;
printf("Counter reset!\n");
}
}
pub fn main() {
var c = Counter.new(10);
c.increment();
c.reset();
}
Static Methods
Static methods do not require a struct instance. They are called on the struct type itself:
pub struct MathUtils {
pub fn square(x: int) int {
return x * x;
}
}
pub fn main() {
printf("%d\n", MathUtils.square(5)); // prints 25
}
Instance Methods
Instance methods operate on a struct instance and can access its fields:
pub fn describe(&self) {
printf("Name: %s, Age: %d\n", self->name, self->age);
}
Reference vs Copy
Cyrus allows controlling how instance methods receive the struct:
- &self → passes a reference to the instance (does not copy)
- self → passes a copy of the instance (moves it)
pub fn print_name(&self) {
printf("%s\n", self->name);
}
pub fn take_copy(self) {
self.name = "applied locally";
}
Attempting to get a reference (&self) from a const-lvalue is not allowed.
The compiler will issue an error to prevent modifying or referencing immutable data.
Pointers and Fat Arrows
In Cyrus, you can have pointers to structs. When accessing fields or calling methods through a pointer, you can use fat pointers (fat arrows) which automatically dereference the pointer.
var user = User { name: "Cyrus", age: 2500 };
var ptr = &user;
ptr->name;
ptr->age;
Using ptr->name or ptr->age is equivalent to {"(*ptr).name"} and {"(*ptr).age"}.
Local Structs
In Cyrus, structs can also be declared locally inside functions. This is useful when the type is only relevant to that function and should not pollute the global scope.
pub fn main() {
struct User {
name: char*;
age: uint;
}
var user = User { name: "Cyrus", age: 2500 };
printf("%s is %d years old.\n", user.name, user.age);
}
Unnamed Structs
Unnamed structs are struct values declared inline without defining a named type. They are useful for quick, one-off configurations or temporary data holders.
fn main() {
var state = struct {
name = "Cyrus",
counter = 0,
};
config.name;
state.counter += 1;
}
Field types are inferred automatically.
You can also specify field types explicitly:
var state = struct {
name: char* = "Cyrus",
counter: int = 0,
};
Unnamed structs cannot define methods.
Global Unnamed Structs
Unnamed structs are not limited to local scope. They can also be declared globally and accessed across functions (if marked public).
const config = struct {
host = "127.0.0.1",
port = 8080
};
pub fn main() {
printf("host: %s\n", config.host);
printf("port: %d\n", config.port);
}

