شمارش‌ها

    شمارش‌ها (enums) در کوروش نوعی را تعریف می‌کنند که می‌تواند یکی از چندین variant باشد. هر variant برچسب‌گذاری شده و ممکن است به صورت اختیاری داده (payload) همراه داشته باشد. Enums برای مدل‌سازی انواع مجموع (sum types)، مدیریت خطا و مقادیر حالت محدود مرکزی هستند.

    کوروش از موارد زیر پشتیبانی می‌کند:

    • Variantهای واحد (Unit variants) (بدون payload)
    • Variantهای چندتایی (Tuple variants) (payload چندتایی)
    • Variantهای ساختاری (Struct variants) (payload فیلددار)
    • Variantهای مقداری (Valued variants) (شبه‌ثابت/اسکالر)

    اعلان پایه Enum

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

    enum Color {
        Red,
        Green,
        Black,
        Custom(int, int, int)
    }
    

    مقادیر را یا با نام enum یا با استفاده از نحو کوتاه .Variant بسازید:

    var c1 = Color.Red;
    var c2 = Color.Custom(1, 2, 3);
    
    var c3: Color = .Green;
    

    انواع Variant

    Variantهای واحد (Unit Variants)

    Variantهای واحد برچسب‌های ساده‌ای هستند که حالت‌های مجزا را بدون هیچ داده اضافی نشان می‌دهند.

    enum ConnectionState {
        Disconnected,
        Connecting,
        Connected,
        Failed
    }
    
    fn is_online(state: ConnectionState) bool {
        switch (state) {
            case .Connected => {
                return true;
            }
            default => {
                return false;
            }
        }
    }
    

    Variantهای چندتایی (Tuple Variants)

    Variantهای چندتایی داده‌های موقعیتی بدون نام را حمل می‌کنند، مانند یک tuple تایپ‌شده متصل به variant.

    enum Task {
        Compute(struct {
            id: uint64,
            priority: int
        }),
        Delay(uint32),
        Range((int, int))
    }
    
    fn process_task(t: Task) {
        switch (t) {
            case .Compute(req) => {
                printf("Compute(id: %llu, priority: %d)\n", req.id, req.priority);
            }
            case .Delay(ms) => {
                printf("Delay(ms: %u)\n", ms);
            }
            case .Range(bounds) => {
                printf("Range(start: %d, end: %d)\n", bounds.0, bounds.1);
            }
            default => {
                printf("Unknown Task\n");
            }
        }
    }
    
    pub fn main() {
        var t = Task.Compute(struct { id: 1024, priority: 5 });
        process_task(t);
    
        t = Task.Range((0, 100));
        process_task(t);
    
        t = Task.Delay(500);
        process_task(t);
    }
    

    Variantهای ساختاری (Struct Variants)

    Variantهای ساختاری یک طرح struct درون‌خطی را مستقیماً درون اعلان variant تعریف می‌کنند. به جای پیچیدن یک نوع موجود، خود variant به عنوان یک طرحواره عمل می‌کند و فیلدهای نام‌دار و تایپ‌شده را فراهم می‌کند.

    enum Error {
        NotFound = struct { id: 1uint32, msg: "not found" },
    
        Custom {
            id: uint32,
            msg: const char*,
            extra: const char*
        }
    }
    
    pub fn main() {
        const err = Error.Custom { id: 2, msg: "custom error message", extra: null };
    
        switch (err) {
            case .NotFound => {
                printf("not found\n");
            }
            case .Custom { id, msg, .. } => {
                printf("id: %d, %s\n", id, msg);
            }
        }
    }
    
    • Custom { ... } یک struct variant با فیلدهای payload نام‌دار است.
    • NotFound در اینجا از یک payload struct مقداری استفاده می‌کند (به زیر مراجعه کنید).

    Variantهای مقداری (Valued Variants / Scalar / Constant-like)

    Variantهای مقداری یک حالت خاص هستند که در آن هر variant یک مقدار ثابت مرتبط دارد. این معمولاً برای enumهای اسکالر استفاده می‌شود.

    type Kind = enum { Unknown = 0, Known = 1 };
    
    import std::libc{printf};
    
    enum ScalarEnum {
        A = 10,
        B = 20,
        C = 30
    }
    
    fn display_scalar_enum(value: ScalarEnum) {
        switch (value) {
            case .A(a) => {
                printf("a(%d) ", a);
            }
            case .B(b) => {
                printf("b(%d) ", b);
            }
            case .C(c) => {
                printf("c(%d) ", c);
            }
        }
    }
    
    pub fn main() {
        const x: ScalarEnum = .A;
    
        display_scalar_enum(x);
        display_scalar_enum(.B);
        display_scalar_enum(.C);
    }
    

    کامپایلر استنتاج می‌کند که همه variantهای ScalarEnum مقدار صحیح دارند و enum را به عنوان یک نوع انتگرال فشرده (int32 به طور پیش‌فرض) در تولید کد پایین‌می‌آورد.

    Variantهای مقداری همچنین مجاز به حمل payloadهای غنی‌تر هستند (مانند NotFound = struct { ... } در مثال قبلی).

    نوع برچسب (Tag Type) و Enumهای اسکالر

    می‌توانید به طور صریح نوع برچسب زیربنایی یک enum را مشخص کنید:

    enum ScalarEnum(uint32) {
        A = 10,
        B = 20,
        C = 30
    }
    

    برای enumهای اسکالر بی‌نام:

    const a: enum(bool) { A = true, B = false } = .A;
    

    نوع برچسب، نمایش سطح ABI برچسب variant را کنترل می‌کند که می‌تواند برای FFI و محدودیت‌های چیدمان سطح پایین مفید باشد.

    Enumهای نام‌دار در مقابل بی‌نام

    کوروش از هر دو enum نام‌دار و بی‌نام پشتیبانی می‌کند.

    Enumهای نام‌دار

    Enumهای نام‌دار یک نوع قابل استفاده مجدد تعریف می‌کنند:

    enum Option<T> {
        Some(T),
        None
    }
    
    pub fn main() {
        var opt: Option<int> = .None;
        display_option(opt);
    
        opt = .Some(10);
        display_option(opt);
    }
    

    توجه: ژنریک‌ها (<T>) در بخش بعدی به طور مفصل معرفی می‌شوند.

    Enumهای بی‌نام

    Enumهای بی‌نام می‌توانند در محل استفاده به صورت درون‌خطی اعلان شوند:

    const mode: enum { Off = 0, On = 1 } = .On;
    
    switch (mode) {
        case .Off(x) => { /* ... */ }
        case .On(x) => { /* ... */ }
    }
    

    این برای مقادیر برچسب‌دار یک‌بار مصرف که تعریف یک نام سطح بالا در آن افراطی است مفید می‌باشد.

    Variantهای ناهمگن (Heterogeneous Variants)

    Enumهای کوروش «انواع مجموع» (sum types) هستند که به هر variant اجازه می‌دهند ساختار payload منحصر‌به‌فرد خود را تعریف کند. شما می‌توانید آزادانه variantهای واحد، variantهای مقداری، variantهای چندتایی و variantهای ساختاری را در یک اعلان واحد ترکیب کنید.

    import std::libc{printf};
    
    enum Color {
        Red,                     // Unit variant
        Green = "green!",        // Valued variant (scalar/constant)
        Custom(uint, uint, uint) // Tuple variant
    }
    
    pub fn main() {
        const color = Color.Green;
    
        switch (color) {
            case .Red => {
                printf("red\n");
            }
            case .Green(value) => {
                printf("%s\n", value);
            }
            case .Custom(a, b, c) => {
                printf("custom(%d, %d, %d)\n", a, b, c);
            }
        }
    }
    
    • استقلال Variant: کامپایلر چیدمان حافظه را مدیریت می‌کند و فضای کافی برای بزرگترین payload ممکن را تضمین می‌کند، در حالی که variant فعال فعلی را از طریق یک برچسب پنهان tracking می‌کند.
    • تطبیق یکپارچه: دستور switch این payloadهای ناهمگن را به طور یکپارچه مدیریت می‌کند و bindings مناسب را بر اساس تعریف خاص variant فراهم می‌کند.
    • ایمنی نوع: کامپایلر تضمین می‌کند که شما فقط سعی می‌کنید به payloadهایی دسترسی پیدا کنید که برای variant خاصی که با آن مطابقت دارید وجود دارند.

    تطبیق روی Enumها

    switch راه اصلی برای تخریب و بازرسی enumها است. نحو الگو به نوع variant بستگی دارد.

    import std::libc{printf};
    
    type Kind = enum { Unknown = 0, Known = 1 };
    
    fn display_kind(value: Kind) {
        switch (value) {
            case .Unknown(x) => {
                printf("%d", x);
            }
            case .Known(x) => {
                printf("%d", x);
            }
        }
    }
    
    pub fn main() {
        const x: Kind = .Unknown;
    
        display_kind(.Known);
        display_kind(.Unknown);
        display_kind(x);
    }
    
    • Variantهای واحد: case .Variant => { ... }
    • Variantهای چندتایی: case .Variant(payload) => { ... }
    • Variantهای ساختاری: case .Variant { field1, field2 } => { ... }
    • Variantهای مقداری/اسکالر: case .Variant(value) => { ... }

    default می‌تواند به عنوان یک شاخه catch-all زمانی که همه variantها لیست نشده‌اند استفاده شود.

    برای variantهای ساختاری، الگوها از نادیده گرفتن، حذف و تغییر نام فیلدها پشتیبانی می‌کنند:

    switch (err) {
        // Ignore a specific exported field
        case .Custom { id, msg, extra: _ } => {
            // 'id' and 'msg' are bound; 'extra' is ignored
        }
    }
    
    switch (err) {
        // Ignore the rest of the fields
        case .Custom { id, msg, .. } => {
            // Only 'id' and 'msg' are bound
        }
    }
    
    switch (err) {
        // Rename a field locally
        case .Custom { id, msg, extra: msg2 } => {
            msg2; // VALID: bound to the 'extra' field
            extra; // ERROR: not found
        }
    }