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);
    }