التعامل مع الثوابت (Constants) والتحويل الآمن بين أنواع البيانات


مرحباً أيها المبرمجون! في هذا الدرس، سنتعلم كيفية التعامل مع الثوابت (Constants) في Go، ونستكشف طرق التحويل الآمن والفعال بين أنواع البيانات لتجنب الأخطاء.

1. تعريف الثوابت (Constants)

الثوابت هي قيم لا تتغير أثناء تنفيذ البرنامج. في Go، يتم تعريف الثوابت باستخدام الكلمة المفتاحية const. يمكن أن تكون الثوابت من أنواع أساسية مثل الأعداد الصحيحة، الأعداد العشرية، السلاسل النصية، والقيم المنطقية.

ملاحظة تقنية: على عكس المتغيرات، لا يمكن تعريف الثوابت باستخدام عامل التشغيل :=. يجب تحديد نوع الثابت بشكل صريح أو تركه يستنتج من القيمة.

لنبدأ بتعريف بعض الثوابت البسيطة:

package main

import "fmt"

const (
    // تعريف ثابت نصي يمثل اسم التطبيق
    AppName = "GoTypeConverter"
    // تعريف ثابت عددي يمثل إصدار التطبيق
    AppVersion = 1.0
    // تعريف ثابت منطقي يحدد ما إذا كان وضع التصحيح مفعلاً
    DebugMode = true
)

func main() {
    fmt.Println("اسم التطبيق:", AppName)
    fmt.Println("إصدار التطبيق:", AppVersion)
    fmt.Println("وضع التصحيح مفعل؟", DebugMode)
}

يمكننا أيضاً استخدام iota لتعريف سلسلة من الثوابت المتزايدة تلقائياً، وهو مفيد جداً للتعدادات (Enums).

package main

import "fmt"

const (
    // iota يبدأ من الصفر ويزداد بواحد لكل ثابت في المجموعة
    Sunday = iota // Sunday = 0
    Monday        // Monday = 1
    Tuesday       // Tuesday = 2
    Wednesday     // Wednesday = 3
    Thursday      // Thursday = 4
    Friday        // Friday = 5
    Saturday      // Saturday = 6
)

func main() {
    fmt.Println("اليوم الأول في الأسبوع (Sunday):", Sunday)
    fmt.Println("اليوم الثالث في الأسبوع (Tuesday):", Tuesday)
}

2. التحويل الصريح بين أنواع البيانات (Explicit Type Conversion)

تتميز Go بنظام أنواع صارم، مما يعني أنها لا تسمح بالتحويل الضمني (Implicit Conversion) بين أنواع البيانات المختلفة بشكل تلقائي لتجنب فقدان البيانات أو الأخطاء غير المتوقعة. يجب أن يتم التحويل بشكل صريح باستخدام Type(value).

ملاحظة تقنية: التحويل الصريح ضروري في Go. محاولة التخصيص بين أنواع غير متوافقة بدون تحويل صريح ستؤدي إلى خطأ في وقت الترجمة (Compile-time error).

لنرى كيف يمكن تحويل عدد صحيح إلى عدد عشري، والعكس:

package main

import "fmt"

func main() {
    var intValue int = 100
    var floatValue float64 = 3.14

    // تحويل عدد صحيح إلى عدد عشري
    // يتم تحويل intValue (100) إلى float64
    convertedFloat := float64(intValue)
    fmt.Printf("تم تحويل %d (int) إلى %f (float64)\n", intValue, convertedFloat)

    // تحويل عدد عشري إلى عدد صحيح
    // يتم تحويل floatValue (3.14) إلى int، مما يؤدي إلى اقتطاع الجزء العشري
    convertedInt := int(floatValue)
    fmt.Printf("تم تحويل %f (float64) إلى %d (int)\n", floatValue, convertedInt)

    // مثال على تحويل بين أنواع الأعداد الصحيحة المختلفة
    var largeInt int64 = 9876543210
    // قد يؤدي التحويل من int64 إلى int إلى فقدان بيانات إذا كانت القيمة تتجاوز نطاق int
    var smallInt int = int(largeInt)
    fmt.Printf("تم تحويل %d (int64) إلى %d (int). لاحظ فقدان البيانات المحتمل.\n", largeInt, smallInt)
}

3. التحويل الآمن باستخدام حزمة strconv

عند التعامل مع المدخلات الخارجية (مثل مدخلات المستخدم، بيانات من ملفات، أو استجابات API)، غالباً ما تكون البيانات في شكل سلاسل نصية تحتاج إلى تحويلها إلى أنواع عددية أو منطقية. هنا يأتي دور حزمة strconv، التي توفر وظائف آمنة للتحويل مع معالجة الأخطاء.

ملاحظة تقنية: وظائف strconv تعيد دائماً قيمتين: القيمة المحولة وخطأ (error). يجب دائماً التحقق من الخطأ لضمان نجاح عملية التحويل.

دعونا نحول سلاسل نصية إلى أعداد صحيحة وعشرية وقيم منطقية، وكذلك العكس:

package main

import (
    "fmt"
    "strconv" // استيراد حزمة strconv للتحويل الآمن
)

func main() {
    // تحويل سلسلة نصية إلى عدد صحيح (int)
    strNum := "12345"
    // Atoi تعني ASCII to Integer
    num, err := strconv.Atoi(strNum)
    if err != nil {
        fmt.Println("خطأ في التحويل من نص إلى عدد صحيح:", err)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %d (نوع: %T)\n", strNum, num, num)
    }

    // تحويل سلسلة نصية غير صالحة إلى عدد صحيح
    invalidStrNum := "abc"
    numInvalid, errInvalid := strconv.Atoi(invalidStrNum)
    if errInvalid != nil {
        fmt.Printf("خطأ متوقع في التحويل من نص غير صالح إلى عدد صحيح: %v\n", errInvalid)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %d (نوع: %T)\n", invalidStrNum, numInvalid, numInvalid)
    }

    // تحويل سلسلة نصية إلى عدد عشري (float64)
    strFloat := "98.765"
    // ParseFloat تحول السلسلة إلى float، ويحدد الرقم 64 حجم البت (float64)
    floatVal, errFloat := strconv.ParseFloat(strFloat, 64)
    if errFloat != nil {
        fmt.Println("خطأ في التحويل من نص إلى عدد عشري:", errFloat)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %f (نوع: %T)\n", strFloat, floatVal, floatVal)
    }

    // تحويل سلسلة نصية إلى قيمة منطقية (bool)
    strBoolTrue := "true"
    boolValTrue, errBoolTrue := strconv.ParseBool(strBoolTrue)
    if errBoolTrue != nil {
        fmt.Println("خطأ في التحويل من نص إلى قيمة منطقية:", errBoolTrue)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %t (نوع: %T)\n", strBoolTrue, boolValTrue, boolValTrue)
    }

    strBoolFalse := "0" // "0", "false" تعتبر خاطئة
    boolValFalse, errBoolFalse := strconv.ParseBool(strBoolFalse)
    if errBoolFalse != nil {
        fmt.Println("خطأ في التحويل من نص إلى قيمة منطقية:", errBoolFalse)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %t (نوع: %T)\n", strBoolFalse, boolValFalse, boolValFalse)
    }

    // تحويل من عدد إلى سلسلة نصية
    someInt := 42
    // Itoa تعني Integer to ASCII
    strFromInt := strconv.Itoa(someInt)
    fmt.Printf("تم تحويل %d إلى \"%s\" (نوع: %T)\n", someInt, strFromInt, strFromInt)

    someFloat := 123.456
    // FormatFloat تحول float إلى نص، مع تحديد التنسيق والدقة وحجم البت
    strFromFloat := strconv.FormatFloat(someFloat, 'f', 2, 64) // 'f' للتنسيق العشري، 2 للدقة (منزلتين عشريتين)
    fmt.Printf("تم تحويل %f إلى \"%s\" (نوع: %T)\n", someFloat, strFromFloat, strFromFloat)
}

الكود النهائي الكامل

هذا هو الكود المجمع الذي يوضح جميع المفاهيم التي ناقشناها في هذا الدرس:

package main

import (
    "fmt"
    "strconv"
)

// تعريف الثوابت باستخدام const و iota
const (
    AppName    = "GoTypeConverter"
    AppVersion = 1.0
    DebugMode  = true

    // iota للتعدادات
    _ = iota // تجاهل القيمة الأولى (عادةً ما تكون 0)
    StatusOK // StatusOK = 1
    StatusError
    StatusWarning
)

func main() {
    fmt.Println("--- الدرس: التعامل مع الثوابت والتحويل الآمن ---")

    // 1. استخدام الثوابت
    fmt.Println("\n--- استخدام الثوابت ---")
    fmt.Println("اسم التطبيق:", AppName)
    fmt.Println("إصدار التطبيق:", AppVersion)
    fmt.Println("وضع التصحيح مفعل؟", DebugMode)
    fmt.Println("حالة العملية الناجحة (StatusOK):", StatusOK)
    fmt.Println("حالة وجود خطأ (StatusError):", StatusError)

    // 2. التحويل الصريح بين أنواع البيانات
    fmt.Println("\n--- التحويل الصريح بين أنواع البيانات ---")
    var intVal int = 50
    var floatVal float32 = 25.75

    // تحويل من int إلى float32
    convertedIntToFloat := float32(intVal)
    fmt.Printf("تم تحويل %d (int) إلى %f (float32)\n", intVal, convertedIntToFloat)

    // تحويل من float32 إلى int (يتم اقتطاع الجزء العشري)
    convertedFloatToInt := int(floatVal)
    fmt.Printf("تم تحويل %f (float32) إلى %d (int)\n", floatVal, convertedFloatToInt)

    // 3. التحويل الآمن باستخدام حزمة strconv (من نص إلى أرقام/منطق)
    fmt.Println("\n--- التحويل الآمن (strconv.Parse) ---")
    strInputNum := "789"
    strInputFloat := "123.45"
    strInputBoolTrue := "true"
    strInputBoolFalse := "false"
    strInputInvalid := "hello"

    // تحويل من نص إلى عدد صحيح (int)
    parsedInt, err := strconv.Atoi(strInputNum)
    if err != nil {
        fmt.Println("خطأ في تحويل الرقم:", err)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %d (نوع: %T)\n", strInputNum, parsedInt, parsedInt)
    }

    // تحويل من نص إلى عدد عشري (float64)
    parsedFloat, err := strconv.ParseFloat(strInputFloat, 64)
    if err != nil {
        fmt.Println("خطأ في تحويل العدد العشري:", err)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %f (نوع: %T)\n", strInputFloat, parsedFloat, parsedFloat)
    }

    // تحويل من نص إلى قيمة منطقية (bool)
    parsedBoolTrue, err := strconv.ParseBool(strInputBoolTrue)
    if err != nil {
        fmt.Println("خطأ في تحويل القيمة المنطقية (true):", err)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %t (نوع: %T)\n", strInputBoolTrue, parsedBoolTrue, parsedBoolTrue)
    }

    parsedBoolFalse, err := strconv.ParseBool(strInputBoolFalse)
    if err != nil {
        fmt.Println("خطأ في تحويل القيمة المنطقية (false):", err)
    } else {
        fmt.Printf("تم تحويل \"%s\" إلى %t (نوع: %T)\n", strInputBoolFalse, parsedBoolFalse, parsedBoolFalse)
    }

    // مثال على تحويل فاشل
    _, err = strconv.Atoi(strInputInvalid)
    if err != nil {
        fmt.Printf("خطأ متوقع عند محاولة تحويل \"%s\" إلى عدد صحيح: %v\n", strInputInvalid, err)
    }

    // 4. التحويل الآمن باستخدام حزمة strconv (من أرقام/منطق إلى نص)
    fmt.Println("\n--- التحويل الآمن (strconv.Format) ---")
    numToConvert := 123
    floatToConvert := 45.678
    boolToConvert := true

    // تحويل من عدد صحيح إلى نص
    strFromInt := strconv.Itoa(numToConvert)
    fmt.Printf("تم تحويل %d إلى \"%s\" (نوع: %T)\n", numToConvert, strFromInt, strFromInt)

    // تحويل من عدد عشري إلى نص
    strFromFloat := strconv.FormatFloat(floatToConvert, 'g', -1, 64) // 'g' يختار أنسب تنسيق، -1 للدقة التلقائية
    fmt.Printf("تم تحويل %f إلى \"%s\" (نوع: %T)\n", floatToConvert, strFromFloat, strFromFloat)

    // تحويل من قيمة منطقية إلى نص
    strFromBool := strconv.FormatBool(boolToConvert)
    fmt.Printf("تم تحويل %t إلى \"%s\" (نوع: %T)\n", boolToConvert, strFromBool, strFromBool)
}

النتيجة المتوقعة

عند تشغيل الكود النهائي، ستحصل على مخرجات مشابهة لما يلي على سطر الأوامر:

--- الدرس: التعامل مع الثوابت والتحويل الآمن ---

--- استخدام الثوابت ---
اسم التطبيق: GoTypeConverter
إصدار التطبيق: 1
وضع التصحيح مفعل؟ true
حالة العملية الناجحة (StatusOK): 1
حالة وجود خطأ (StatusError): 2

--- التحويل الصريح بين أنواع البيانات ---
تم تحويل 50 (int) إلى 50.000000 (float32)
تم تحويل 25.750000 (float32) إلى 25 (int)

--- التحويل الآمن (strconv.Parse) ---
تم تحويل "789" إلى 789 (نوع: int)
تم تحويل "123.45" إلى 123.450000 (نوع: float64)
تم تحويل "true" إلى true (نوع: bool)
تم تحويل "false" إلى false (نوع: bool)
خطأ متوقع عند محاولة تحويل "hello" إلى عدد صحيح: strconv.Atoi: parsing "hello": invalid syntax

--- التحويل الآمن (strconv.Format) ---
تم تحويل 123 إلى "123" (نوع: string)
تم تحويل 45.678000 إلى "45.678" (نوع: string)
تم تحويل true إلى "true" (نوع: string)