ساختارها

    ساختارها (structs) در کوروش انواع ترکیبی تعریف‌شده توسط کاربر هستند که داده‌های مرتبط را گروه‌بندی می‌کنند. آنها مشابه struct در C/C++ هستند اما از قوانین سیستم نوع کوروش پیروی می‌کنند.

    تعریف یک ساختار

    از کلیدواژه struct به همراه نام و فیلدها استفاده کنید:

    struct User {
        pub name: char*; // public field
        pub age: uint; // public field
        king: bool; // private field
    }
    

    فیلدهای علامت‌گذاری شده با pub می‌توانند از خارج struct قابل دسترسی باشند. فیلدهای بدون pub خصوصی هستند و فقط درون struct یا متدهای آن قابل دسترسی می‌باشند.

    مقداردهی اولیه یک ساختار

    ساختارها می‌توانند با استفاده از لیترال‌های struct مقداردهی اولیه شوند:

    var user = User {
        name: "Cyrus",
        age: 2500,
        king: true
    };
    

    دسترسی به فیلدهای ساختار

    می‌توانید با استفاده از عملگر نقطه (.) به فیلدهای struct دسترسی پیدا کنید:

    printf("%s is %d years old.\n", user.name, user.age);
    

    user.king در خارج از struct قابل دسترسی نیست زیرا خصوصی است.

    متدها

    ساختارها می‌توانند توابعی را درون بدنه خود تعریف کنند که متد (method) نامیده می‌شوند. متدها به structها اجازه می‌دهند رفتار را همراه با داده محصور کنند.

    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)

    متدهای ایستا به یک نمونه struct نیاز ندارند. آنها بر روی خود نوع struct فراخوانی می‌شوند:

    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)

    متدهای نمونه روی یک نمونه struct عمل می‌کنند و می‌توانند به فیلدهای آن دسترسی داشته باشند:

    pub fn describe(&self) {
        printf("Name: %s, Age: %d\n", self->name, self->age);
    }
    

    ارجاع در مقابل کپی

    کوروش امکان کنترل نحوه دریافت struct توسط متدهای نمونه را فراهم می‌کند:

    • &self → یک ارجاع (reference) به نمونه ارسال می‌کند (کپی نمی‌کند)
    • self → یک کپی از نمونه ارسال می‌کند (آن را جابه‌جا می‌کند)
    pub fn print_name(&self) {
        printf("%s\n", self->name);
    }
    
    pub fn take_copy(self) {
        self.name = "applied locally";
    }
    

    تلاش برای دریافت ارجاع (&self) از یک const-lvalue مجاز نیست.
    کامپایلر برای جلوگیری از تغییر یا ارجاع به داده‌های تغییرناپذیر خطا صادر می‌کند.

    اشاره‌گرها و پیکان‌های چاق (Fat Arrows)

    در کوروش، می‌توانید اشاره‌گرهایی به structها داشته باشید. هنگام دسترسی به فیلدها یا فراخوانی متدها از طریق یک اشاره‌گر، می‌توانید از پیکان‌های چاق (fat pointers/arrows) استفاده کنید که به طور خودکار اشاره‌گر را dereference می‌کنند.

    var user = User { name: "Cyrus", age: 2500 };
    var ptr = &user;
    
    ptr->name;
    ptr->age;
    

    استفاده از ptr->name یا ptr->age معادل {"(*ptr).name"} و {"(*ptr).age"} است.

    ساختارهای بی‌نام (Unnamed Structs)

    ساختارهای بی‌نام مقادیر structی هستند که بدون تعریف یک نوع نام‌دار به صورت درون‌خطی اعلان می‌شوند. آنها برای پیکربندی‌های سریع و یک‌بار مصرف یا نگهدارنده‌های موقت داده مفید هستند.

    fn main() {
        var state = struct {
            name: "Cyrus",
            counter: 0,
        };
    
        state.counter += 5;
    }
    
    • ساختارهای بی‌نام نمی‌توانند متد تعریف کنند.
    • همه فیلدها در یک struct بی‌نام به طور پیش‌فرض عمومی هستند و دید آنها قابل تغییر نیست.

    ساختارهای بی‌نام سراسری

    ساختارهای بی‌نام محدود به scope محلی نیستند. آنها همچنین می‌توانند به صورت سراسری اعلان شوند و در سراسر توابع قابل دسترسی باشند (در صورت عمومی علامت‌گذاری).

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

    سازگاری ساختارهای نام‌دار و بی‌نام

    ساختارهای نام‌دار و بی‌نام در صورتی سازگار در نظر گرفته می‌شوند که دارای نام فیلدها و انواع فیلدهای متناظر دقیقاً یکسانی باشند. این سازگاری امکان تبدیل روان بین انواع struct رسمی و ساختارهای درون‌خطی را فراهم می‌کند.

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

    به طور مشابه، یک مقدار struct بی‌نام می‌تواند برای مقداردهی اولیه یا تخصیص به یک نمونه struct نام‌دار استفاده شود:

    pub fn main() {
        const inst: MyStruct = struct {
            a: 10,
            b: 3.14,
            c: 'z'
        };
    
        printf("%d %f %c", inst.a, inst.b, inst.c);
    }
    

    ثابت‌بودن پارامترهای متد

    در کوروش، می‌توانید تغییرناپذیری را هم برای اشاره‌گر نمونه و هم برای آرگومان‌های منفرد در یک متد اعمال کنید. استفاده از &const self تضمین می‌کند که متد نمی‌تواند هیچ فیلدی از struct را تغییر دهد و عملاً آن را به یک متد فقط‌خواندنی تبدیل می‌کند.

    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
    }