Structs

    Structs in Cyrus are user-defined composite types that allow grouping 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 {
        name: char*;
        age: uint;
        king: bool;
    }
    

    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:

    user.name; // Output: Cyrus
    user.age;  // Output: 2500
    user.king; // Output: true
    

    Methods

    Structs can define functions inside their body, called methods.

    pub struct Logger {
        pub group: char*;
    
        pub fn new(group: char*) Logger { 
            Logger.logger_initialized();
            return Logger { group: group };
        }
    
        inline fn logger_initialized() {
            libc::fprintf(libc::stdout, "Logger init.\n");
        }
    
        pub fn logf(&self, format: char*) {
            libc::printf("[info][%s] %s\n", self->group, format);
        }
    
        pub inline fn errorf(self, format: char*) {
            libc::fprintf(libc::stderr, "[error][%s] %s\n", self->group, format);
        }
    }
    

    Static Methods

    Static methods do not require a struct instance. Called on the struct type itself. It often used as constructor or helper functions.

    pub fn new(group: char*) Logger {
        return Logger { group: group };
    }
    

    Instance Methods

    Instance methods operate on a struct instance and can access its fields.

    pub fn logf(&self, format: char*) {
        libc::printf("[info][%s] %s\n", self->group, format);
    }
    
    pub fn errorf(self, format: char*) {
        libc::fprintf(libc::stderr, "[error][%s] %s\n", self->group, format);
    }
    

    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 logf(&self, format: char*) { ... } // reference
    pub fn errorf(self, format: char*) { ... } // copy
    

    Note: 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.


    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; // Output: Cyrus
    ptr->age; // Output: 2500
    

    Note: 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.

    fn main() {
        struct User {
            name: char*;
            age: uint;
        }
    
        var user = User { name: "Cyrus", age: 2500 }; 
        libc::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.


    Typed Unnamed Struct

    You can also specify field types explicitly:

    var state = struct {
        name: char* = "Cyrus",
        counter: int = 0,
    };
    

    Note: 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).

    pub const CONFIG = struct {
        host = "127.0.0.1",
        port = 8080
    };
    
    fn main() {
        printf("host: %s\n", CONFIG.host);
        printf("port: %d\n", CONFIG.port);
    }