فهم نطاق المتغيرات في Golang: المتغيرات المحلية والعامة

12, 2025 · 5 دقيقة قراءة
رسم كرتوني لتميمة Go (الغوفر) تشرح على سبورة الفرق بين نطاق المتغيرات المحلية والعامة في Golang
blog

مقدمة

في Golang، يحدد نطاق المتغير (Variable Scope) الأماكن التي يمكن الوصول فيها إلى المتغير وتعديله داخل البرنامج. قد يبدو الموضوع للوهلة الأولى من أساسيات المبتدئين — إلى أن تكتشف أن متغيرًا عامًا ظننت أنك حدّثته لم يتغير أصلًا، والسبب كله علامة := واحدة! لذا فإن فهم النطاق المحلي والنطاق العام أمر جوهري لكتابة كود سهل الصيانة وخالٍ من الأخطاء. وعندما تعمل عبر أكثر من package، فإن التعامل الصحيح مع المتغيرات العامة يضمن لك كودًا معياريًا قابلًا لإعادة الاستخدام.

سنغطي في هذا المقال:

  • المتغيرات المحلية والعامة في Golang
  • تحديث المتغيرات العامة داخل الدوال
  • استخدام المتغيرات العامة عبر أكثر من package
  • أفضل الممارسات لإدارة المتغيرات العامة

المتغيرات المحلية والعامة في Golang

المتغيرات المحلية: نطاق الدالة أو الـ Block

يكون المتغير محليًا (Local) عندما يُعرَّف داخل دالة (Function) أو حلقة تكرار أو block. ولا يمكن الوصول إليه إلا داخل تلك الدالة أو ذلك الـ block تحديدًا، ولا يمكن استخدامه في أي مكان آخر.

مثال: نطاق المتغير المحلي

package main

import "fmt"

func main() {
    x := 10  // Local variable in main()
    fmt.Println(x) // Accessible within main()

    if true {
        y := 20 // Local to the if block
        fmt.Println(y)
    }

    // fmt.Println(y) // ERROR: y is out of scope here
}
  • المتغير x محلي داخل main().
  • المتغير y محلي داخل block الـ if ولا يمكن الوصول إليه خارجه.

المتغيرات العامة: نطاق الـ Package

المتغير العام (Global) هو المتغير المُعرَّف خارج أي دالة، ويمكن الوصول إليه من أي مكان داخل الـ package.

مثال: متغير عام

package main

import "fmt"

var globalVar = 100 // Global variable

func main() {
    fmt.Println("Before update:", globalVar)
    updateGlobalVar()
    fmt.Println("After update:", globalVar) // Value changed globally
}

func updateGlobalVar() {
    globalVar = 200 // Modifying global variable
}
  • المتغير globalVar مُعرَّف خارج أي دالة، ما يجعله متاحًا لكل الدوال داخل الـ package.
  • الدالة updateGlobalVar() تعدّل قيمة globalVar، وينعكس التغيير على مستوى البرنامج كله.

تحديث متغير عام داخل دالة

يمكن تحديث المتغيرات العامة مباشرة داخل الدوال — إلا إذا حجبها متغير محلي يحمل الاسم نفسه (وهذا ما يُعرف بالـ shadowing).

مثال: تعديل متغير عام مباشرة

var counter = 0 // Global variable

func increment() {
    counter++ // Modifies the global variable
}

func main() {
    fmt.Println("Before increment:", counter)
    increment()
    fmt.Println("After increment:", counter) // Value updated globally
}

الخطأ الشائع: الـ Shadowing

من الأخطاء الشائعة إعادة تعريف متغير عام باستخدام :=، وهو ما يُنشئ متغيرًا محليًا جديدًا بدلًا من تعديل المتغير العام. هذا هو النوع من الأخطاء الذي يبدو وكأنه يعمل: الدالة تطبع القيمة الجديدة، بينما يحتفظ المتغير العام بقيمته القديمة في صمت.

var data = "Global"

func changeData() {
    data := "Local" // Creates a new local variable, doesn't modify the global one
    fmt.Println("Inside function:", data)
}

func main() {
    fmt.Println("Before:", data)
    changeData()
    fmt.Println("After:", data) // Still "Global"
}
  • الحل: استخدم data = "Modified Global" بدلًا من := لتحديث المتغير العام.

استخدام المتغيرات العامة عبر أكثر من Package

المتغيرات العامة المشتركة بين أكثر من package يجب أن تكون مُصدَّرة (Exported) — أي أن يبدأ اسمها بحرف كبير — وأن تُستورد بالشكل الصحيح.

مثال على هيكل المشروع

project/
│── main.go
│── config/
│   ├── config.go

الخطوة 1: تعريف متغير عام داخل package الـ config

أنشئ الملف config/config.go وعرّف فيه متغيرًا مُصدَّرًا:

package config

import "fmt"

// Exported global variable (uppercase first letter)
var AppName = "My Awesome App"

func PrintConfig() {
    fmt.Println("App Name:", AppName)
}

الخطوة 2: الوصول إلى المتغير العام من main.go

استورد package الـ config واستخدم المتغير العام:

package main

import (
    "fmt"
    "project/config" // Importing the package
)

func main() {
    fmt.Println("Application:", config.AppName) // Accessing global variable
    config.PrintConfig()
}
  • المتغير AppName متاح هنا لأنه مُصدَّر.
  • الدالة config.PrintConfig() تطبع قيمة المتغير.

الخطوة 3: تعديل المتغير العام من package آخر

المتغيرات العامة داخل أي package يمكن تحديثها من packages أخرى ما دامت مُصدَّرة.

package main

import (
    "fmt"
    "project/config"
)

func main() {
    fmt.Println("Before:", config.AppName)

    // Modifying global variable
    config.AppName = "Updated App Name"

    fmt.Println("After:", config.AppName)
}

منع التعديل باستخدام دوال الـ Getter

إذا أردت حماية متغير عام من التعديل خارج الـ package الخاص به، اجعله خاصًا (Private) ووفّر دالة getter للوصول إليه.

package config

// Unexported variable
var appName = "My Secure App"

// Getter function
func GetAppName() string {
    return appName
}

الآن لا يمكن تعديل appName من main.go، لكن يظل بالإمكان قراءته عبر config.GetAppName().

استخدام sync.Once للتهيئة

استخدم sync.Once لضمان أن تهيئة (Initialization) المتغير العام تحدث مرة واحدة فقط.

package config

import "sync"

var (
    once    sync.Once
    AppName string
)

func InitConfig() {
    once.Do(func() {
        AppName = "Initialized App Name"
    })
}
package main

import (
    "fmt"
    "project/config"
)

func main() {
    config.InitConfig()
    fmt.Println("Application:", config.AppName)
}

أفضل الممارسات للتعامل مع المتغيرات العامة

  • قلّل من المتغيرات العامة: الإفراط فيها يجعل صيانة الكود أصعب مع الوقت.
  • استخدم دوال getter/setter: إذا كان التعديل يجب أن يكون مقيدًا، وفّر دوال بدلًا من كشف المتغير نفسه.
  • استخدم sync.Once للتهيئة: تجنّب التهيئة المتكررة (وسباقات التهيئة المتزامنة).
  • تجنّب التبعيات غير الضرورية بين الـ packages: ضع المتغيرات العامة في packages مخصصة لتفادي الاستيراد الدائري (Circular Imports).

الخلاصة

جدول يلخّص أنواع نطاق المتغيرات في Go: المحلي والعام وعبر الـ packages

نطاق المتغيرات في Golang يتبع قواعد واضحة: المتغير محلي افتراضيًا، ويصبح متاحًا على مستوى الـ package عندما يُعرَّف في المستوى الأعلى، ولا يُرى عبر الـ packages الأخرى إلا إذا كان مُصدَّرًا.

وبالالتزام بأفضل الممارسات، ستتمكن من كتابة برامج Go قابلة للتوسع، معيارية، وسهلة الصيانة. 🚀

أحمد كمال عمارة
Authors
الرئيس التنفيذي للتقنية · التقنية المالية وهندسة المنصات
أنا رئيس تنفيذي للتقنية بخبرة تتجاوز 18 عامًا في بناء وتوسيع المؤسسات التقنية عبر قطاعات التقنية المالية المنظَّمة والمدفوعات والتجارة الإلكترونية ومنصات المؤسسات. أعمل اليوم رئيسًا للتقنية في ترميز كابيتال، وهي شركة تقنية مالية سعودية تعمل في التمويل والاستثمار عبر الصكوك الإسلامية، حيث انتقلت بالمنصة من فريق من أربعة أشخاص قبل الإطلاق إلى أكثر من 40 موظفًا وإطلاق مرخَّص من هيئة السوق المالية والبنك المركزي السعودي. أكتب عن هندسة الأنظمة الخلفية وقواعد البيانات والأمن والخدمات المصغّرة والبنية التحتية للتقنية المالية — المشكلات التي أواجهها فعليًا في بيئة الإنتاج.