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's type system rules.
Defining a Struct
Use the struct keyword, followed by the name and fields:
struct User {
pub name: char*; // public field
pub age: uint; // public field
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 SimpleCounter {
pub count: int;
pub fn new(count: int) Self {
return Self { count };
}
pub fn increment(&self) {
self->count++;
}
pub fn reset(&self) {
self->count = 0;
}
}
pub fn main() {
var c = SimpleCounter.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"}.
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,
};
state.counter += 5;
}
- Unnamed structs cannot define methods.
- All fields in an unnamed struct are public by default and their visibility cannot be changed.
Global Unnamed Structs
Unnamed structs are not limited to local scope. They can also be declared globally and accessed across functions (if marked public).
pub 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);
}
Named & Unnamed Structs Compatibility
Named and unnamed structs are considered compatible if they possess the exact same field names and corresponding field types. This compatibility allows for fluid conversion between formal struct types and inline structures.
struct MyStruct {
pub a: int;
pub b: float64;
pub c: char;
}
pub fn main() {
const obj = MyStruct { a: 10, b: 3.14, c: 'z' };
// compatible because field names and types match exactly!
const temp: struct { a: int, b: float64, c: char } = obj;
printf("%d %f %c", temp.a, temp.b, temp.c);
}
Similarly, an unnamed struct value can be used to initialize or be assigned to a named struct instance:
pub fn main() {
const inst: MyStruct = struct {
a: 10,
b: 3.14,
c: 'z'
};
printf("%d %f %c", inst.a, inst.b, inst.c);
}
Method Parameter Constness
In Cyrus, you can enforce immutability for both the instance pointer and the individual arguments within a method.
Using &const self ensures that the method cannot modify any fields of the struct, effectively making it a read-only method.
struct Wallet {
pub balance: float64;
pub fn calculate_projection(const self, const adjustment: float64) float64 {
self->balance += adjustment; // ERROR!
adjustment = 0.0; // ERROR!
return self.balance + adjustment;
}
pub fn deposit(self, amount: float64) {
self.balance += amount; // ALLOWED
}
}
pub fn main() {
const my_wallet = Wallet { balance: 1000.0 };
const next_month = my_wallet.calculate_projection(500.0); // ALLOWED
my_wallet.deposit(50.0); // ERROR: cannot call mutable method on a const instance
}

