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

مقدمة
في 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).
الخلاصة

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