"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > استخدام الخرائط بأمان في Golang: الاختلافات في الإعلان والتهيئة

استخدام الخرائط بأمان في Golang: الاختلافات في الإعلان والتهيئة

تم النشر بتاريخ 2024-11-08
تصفح:953

Safely using Maps in Golang: Differences in declaration and initialization

مقدمة

كنت أعمل هذا الأسبوع على إحدى حزم واجهة برمجة التطبيقات لـ golang، والتي تعاملت مع إرسال طلبات النشر بقيم URL المشفرة، وتعيين ملفات تعريف الارتباط، وجميع الأشياء الممتعة. ومع ذلك، أثناء قيامي بإنشاء النص، كنت أستخدم نوع url.Value لإنشاء النص، واستخدامه لإضافة وتعيين أزواج القيمة الرئيسية. ومع ذلك، كنت أتلقى خطأً مرجعيًا سلكيًا في بعض الأجزاء، وأعتقد أن ذلك كان بسبب بعض المتغيرات التي قمت بتعيينها يدويًا. ومع ذلك، من خلال تصحيح الأخطاء بشكل أقرب، اكتشفت مأزقًا شائعًا أو ممارسة سيئة تتمثل في مجرد الإعلان عن نوع ما مع تهيئته مما يتسبب في عدم وجود أخطاء مرجعية.

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

ما هي الخريطة في جولانج؟

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

كيفية إنشاء الخرائط في جولانج

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

package main

import "fmt"

func main() {
    words := "hello how are you"
    letters := map[string]int{}

    for _, word := range words {
        wordCount[word]  
    }

    fmt.Println("Word counts:")
    for word, count := range wordCount {
        fmt.Printf("%s: %d\n", word, count)
    }
}
$ go run main.go

Word counts:
e: 2
 : 3
w: 1
r: 1
y: 1
u: 1
h: 2
l: 2
o: 3
a: 1

لذلك، من خلال تهيئة الخريطة كـmap[string]int{} سوف تحصل على خريطة فارغة. يمكن بعد ذلك استخدام هذا لملء المفاتيح والقيم، ونكرره عبر السلسلة، ولكل حرف (رون) نلقي بايت الحرف هذا في السلسلة ونزيد القيمة، وتكون القيمة الصفرية لـ int هي 0، لذلك افتراضيًا إذا لم يكن المفتاح موجودًا، فسيكون صفرًا، إنه سيوف ذات حدين، لكننا لا نعرف أبدًا أن المفتاح موجود في الخريطة بقيمة 0 أو أن المفتاح غير موجود ولكن القيمة الافتراضية هي 0. لذلك تحتاج للتحقق مما إذا كان المفتاح موجودًا في الخريطة أم لا.

لمزيد من التفاصيل، يمكنك مراجعة منشور خرائط Golang الخاص بي بالتفصيل.

الفرق بين الإعلان والتهيئة

هناك فرق في الإعلان عن أي متغير وتهيئته في لغة البرمجة ويجب أن يفعل الكثير مع تنفيذ النوع الأساسي. في حالة أنواع البيانات الأساسية مثل int، وstring، وfloat، وما إلى ذلك، توجد قيم افتراضية/صفرية، بحيث تكون مماثلة للإعلان عن المتغيرات وتهيئتها. ومع ذلك، في حالة الخرائط والشرائح، فإن الإعلان يتأكد فقط من أن المتغير متاح لنطاق البرنامج، ولكن للتهيئة يتم تعيينه على قيمته الافتراضية/الصفر أو القيمة الفعلية التي يجب تعيينها.

لذلك، الإعلان ببساطة يجعل المتغير متاحًا ضمن نطاق البرنامج. بالنسبة للخرائط والشرائح، فإن الإعلان عن متغير دون تهيئة يؤدي إلى تعيينه على صفر، مما يعني أنه لا يشير إلى أي ذاكرة مخصصة ولا يمكن استخدامه مباشرة.

بينما تقوم التهيئة بتخصيص الذاكرة وتعيين المتغير إلى حالة قابلة للاستخدام. بالنسبة للخرائط والشرائح، تحتاج إلى تهيئتها بشكل صريح باستخدام بناء جملة مثل myMap = make(map[keyType]valueType) أوlice = []type{}. بدون هذه التهيئة، ستؤدي محاولة استخدام الخريطة أو الشريحة إلى حدوث أخطاء في وقت التشغيل، مثل الذعر عند الوصول إلى خريطة أو شريحة صفرية أو تعديلها.

دعونا نلقي نظرة على قيم الخريطة عندما يتم الإعلان عنها/تهيئتها/عدم تهيئتها.

تخيل أنك تقوم بإنشاء مدير تكوين يقرأ الإعدادات من الخريطة. سيتم الإعلان عن الخريطة عالميًا ولكن سيتم تهيئتها فقط عند تحميل التكوين.

  1. تم الإعلان عنها ولكن لم تتم تهيئتها

يوضح الكود أدناه الوصول إلى الخريطة التي لم تتم تهيئتها.

package main

import (
    "fmt"
    "log"
)

// Global map to store configuration settings
var configSettings map[string]string // Declared but not initialized

func main() {
    // Attempt to get a configuration setting before initializing the map
    serverPort := getConfigSetting("server_port")
    fmt.Printf("Server port: %s\n", serverPort)
}

func getConfigSetting(key string) string {
    if configSettings == nil {
        log.Fatal("Configuration settings map is not initialized")
    }
    value, exists := configSettings[key]
    if !exists {
        return "Setting not found"
    }
    return value
}
$ go run main.go
Server port: Setting not found
  1. تم الإعلان والتهيئة في نفس الوقت

يوضح الكود أدناه إمكانية الوصول إلى الخريطة التي تتم تهيئتها في نفس الوقت.

package main

import (
    "fmt"
    "log"
)

// Global map to store configuration settings
var configSettings = map[string]string{
    "server_port":  "8080",
    "database_url": "localhost:5432",
}

func main() {
    serverPort := getConfigSetting("server_port")
    fmt.Printf("Server port: %s\n", serverPort)
}

func getConfigSetting(key string) string {
    value, exists := configSettings[key]
    if !exists {
        return "Setting not found"
    }
    return value
}
$ go run main.go
Server port: 8080
  1. تم الإعلان عنها وتهيئتها لاحقًا

يوضح الكود أدناه إمكانية الوصول إلى الخريطة التي تتم تهيئتها لاحقًا.

package main

import (
    "fmt"
    "log"
)

// Global map to store configuration settings
var configSettings map[string]string // declared but not initialized

func main() {
    // Initialize configuration settings
    initializeConfigSettings()
    // if the function is not called, the map will be nil

    // Get a configuration setting safely
    serverPort := getConfigSetting("server_port")
    fmt.Printf("Server port: %s\n", serverPort)
}

func initializeConfigSettings() {
    if configSettings == nil {
        configSettings = make(map[string]string) // Properly initialize the map
        configSettings["server_port"] = "8080"
        configSettings["database_url"] = "localhost:5432"
        fmt.Println("Configuration settings initialized")
    }
}

func getConfigSetting(key string) string {
    if configSettings == nil {
        log.Fatal("Configuration settings map is not initialized")
    }
    value, exists := configSettings[key]
    if !exists {
        return "Setting not found"
    }
    return value
}
$ go run main.go
Configuration settings initialized
Server port: 8080

في الكود أعلاه، أعلنا عن إعدادات تكوين الخريطة العالمية ولكننا لم نقوم بتهيئتها في تلك المرحلة، حتى أردنا الوصول إلى الخريطة. نقوم بتهيئة الخريطة في الوظيفة الرئيسية، يمكن أن تكون هذه الوظيفة الرئيسية أجزاء محددة أخرى من الكود، والمتغير العام configSettings خريطة من جزء آخر من الكود، من خلال تهيئتها في النطاق المطلوب، نمنعها من التسبب في مؤشر لا شيء أخطاء الوصول. نقوم بتهيئة الخريطة فقط إذا كانت صفرًا، أي لم تتم تهيئتها في مكان آخر من الكود. يمنع هذا تجاوز الخريطة/مسح مجموعة التكوين من أجزاء أخرى من النطاق.

المزالق في الوصول إلى الخرائط غير المهيأة

ولكن نظرًا لأنه يتعامل مع المؤشرات، فإنه يأتي مع مخاطره الخاصة مثل عدم الوصول إلى المؤشرات عندما لا تتم تهيئة الخريطة.

دعونا نلقي نظرة على مثال، حالة حقيقية حيث قد يحدث هذا.

package main

import (
    "fmt"
    "net/url"
)

func main() {
        var vals url.Values
        vals.Add("foo", "bar")
        fmt.Println(vals)
}

سيؤدي هذا إلى حالة من الذعر أثناء التشغيل.

$ go run main.go
panic: assignment to entry in nil map

goroutine 1 [running]:
net/url.Values.Add(...)
        /usr/local/go/src/net/url/url.go:902
main.main()
        /home/meet/code/playground/go/main.go:10  0x2d
exit status 2

وذلك لأن url.Values ​​عبارة عن خريطة لسلسلة وقائمة بقيم السلسلة. نظرًا لأن النوع الأساسي عبارة عن خريطة للقيم، وفي المثال، قمنا فقط بتعريف المتغير vals بالنوع url.Values، فسوف يشير إلى مرجع لا شيء، ومن هنا جاءت الرسالة حول إضافة القيمة إلى النوع. لذلك، من الممارسات الجيدة استخدام make أثناء الإعلان عن نوع بيانات الخريطة أو تهيئته. إذا لم تكن متأكدًا من أن النوع الأساسي هو خريطة، فيمكنك استخدام النوع {} لتهيئة قيمة فارغة من هذا النوع.

package main

import (
    "fmt"
    "net/url"
)

func main() {
        vals := make(url.Values)
        // OR
        // vals := url.Values{}
        vals.Add("foo", "bar")
        fmt.Println(vals)
}
$ go run urlvals.go
map[foo:[bar]]
foo=bar

يوصى أيضًا فريق golang باستخدام وظيفة الإنشاء أثناء تهيئة الخريطة. لذلك، إما استخدم make للخرائط والشرائح والقنوات، أو قم بتهيئة متغير القيمة الفارغة باستخدام النوع {}. كلاهما يعمل بشكل مشابه، ولكن الأخير ينطبق بشكل عام على الهياكل أيضًا.

خاتمة

يعد فهم الفرق بين الإعلان عن الخرائط وتهيئتها في Golang أمرًا ضروريًا لأي مطور، ليس فقط في golang، ولكن بشكل عام. كما اكتشفنا، فإن مجرد الإعلان عن متغير الخريطة دون تهيئته يمكن أن يؤدي إلى أخطاء في وقت التشغيل، مثل الذعر عند محاولة الوصول إلى خريطة لا شيء أو تعديلها. تضمن تهيئة الخريطة تخصيصها بشكل صحيح في الذاكرة وجاهزتها للاستخدام، وبالتالي تجنب هذه المخاطر.

من خلال اتباع أفضل الممارسات - مثل استخدام وظيفة الإنشاء أو التهيئة باستخدام النوع {} - يمكنك منع المشكلات الشائعة المتعلقة بالخرائط غير المهيأة. تأكد دائمًا من تهيئة الخرائط والشرائح بشكل صريح قبل استخدامها للحماية من عدم وجود مرجعيات مؤشر غير متوقعة

شكرًا لك على قراءة هذا المنشور، إذا كان لديك أي أسئلة وتعليقات واقتراحات، فلا تتردد في تركها في التعليقات.

ترميز سعيد :)

بيان الافراج تم إعادة إنتاج هذه المقالة على: https://dev.to/mr_destructive/safely-using-maps-in-golang-differences-in-declaration-and-initialization-2jfi?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ Study_golang@163 .com لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3