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.
Use the struct
keyword, followed by the name and fields:
struct User {
name: char*;
age: uint;
king: bool;
}
Structs can be initialized using struct literals:
#user = User {
name: "Cyrus",
age: 2500,
king: true
};
You can access struct fields using the dot (.
) operator:
user.name; // Output: Cyrus
user.age; // Output: 2500
user.king; // Output: true
Structs can define functions inside their body, called methods. Methods can be public, private or inline.
public struct Logger {
group: char*;
public func new(group: char*) Logger {
Logger.logger_initialized();
return Logger { group: group };
}
inline func logger_initialized() {
libc::fprintf(libc::stdout, "Logger init.\n");
}
public func logf(&self, format: char*) {
libc::printf("[info][%s] %s\n", self->group, format);
}
public inline func errorf(self, format: char*) {
libc::fprintf(libc::stderr, "[error][%s] %s\n", self->group, format);
}
}
Static methods do not require a struct instance. Called on the struct type itself. It often used as constructor or helper functions.
public func new(group: char*) Logger {
return Logger { group: group };
}
Instance methods operate on a struct instance and can access its fields.
public func logf(&self, format: char*) {
libc::printf("[info][%s] %s\n", self->group, format);
}
public func errorf(self, format: char*) {
libc::fprintf(libc::stderr, "[error][%s] %s\n", self->group, format);
}
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)public func logf(&self, format: char*) { ... } // reference
public func 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.
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.
#user = User { name: "Cyrus", age: 2500 };
#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.
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.
func main() {
struct User {
name: char*;
age: uint;
}
#user = User { name: "Cyrus", age: 2500 };
libc::printf("%s is %d years old.\n", user.name, user.age);
}
Unnamed structs are struct values declared inline without defining a named type. They are useful for quick, one-off configurations or temporary data holders.
func main() {
#state = struct {
name = "Cyrus",
counter = 0,
};
config.name;
state.counter += 1;
}
Field types are inferred automatically.
You can also specify field types explicitly:
#state = struct {
name: char* = "Cyrus",
counter: int = 0,
};
Note: Unnamed structs cannot define methods.
Unnamed structs are not limited to local scope. They can also be declared globally and accessed across functions (if marked public).
public config = const struct {
host = "127.0.0.1",
port = 8080
};
func main() {
printf("host: %s\n", config.host);
printf("port: %d\n", config.port);
}