پوینترها در زبان گو

منبع :)پوینترها یک ابزار اولیه اما خیلی قدرتمند هستند که معمولا به عنوان یک چیز سخت و مزخرف ازش یاد میشه موقع برنامه نویسی !پوینترها ساخته شدند برای تسریع و کمک به جهت جابجایی دیتا در کدها بدون اینکه نیاز باشه از یک متغیر کپی گرفته یا ساخته بشه، خوب که چی بشه دوست من !؟ جوابش سادست => مصرف کمتر مموری :)در برخی زبان ها این concept کلا دوری شده ازش، بعضی ها هم تقریبی سعی کردن مدیریتش کنند اما در زبان گو پوینترها به معنای واقعی یک ابزار خوب برای بهبود performance ازش استفاده میشه.خوب بریم سراغ این دوستمون پوینتر یک پوینتر متغیری هستش که اشاره میکنه به محل خاصی از مموری که مقداری در اونجا ذخیره شده، بنابراین شما یک متغیری را ایجاد کرده و مقداری در این قرار میدهید و خیلی ساده به جای اینکه مقدار واقعی اونو بخواین بخونین از آدرسش می تونین استفاده کنین.به کد زیر توجه کنیدpackage main import ( &quotfmt&quot ) func main ( ) { myString := &quota string value&quot fmt.Println(myString) myInteger := 1 fmt.Println(myInteger) }ما دوتا متغیر ساختیم و پرینتش کردین ! هیچ کار خاصی و عجیبی هم انجام ندادیم هنوزاگر ما یک متغیری را به یک تابعی پاس بدیم، زبان میاد یک کپی از اونا میگیره و در داخل تایع ازش استفاده میکنه مثل این:func myFunction(s string){ fmt.Println(s) }func main() { myOtherString := &quotanother message here&quot myFunction(myOtherString) }وقتیکه تابع myFunction مقدار رشته ای s دریافت میکنه و پرینتش میکنه روی کنسول گولنک (و اکثر زبان ها) یک کپی از مقداری که ارسال شده درست میکنه در داخل تابع، وقتیکه تابع کارش به آخر میرسه اینجاست که Garbage collector میاد به سراغ این متغیر ها.خوب حالا همون کارو با پوینتر انجامش میدیمfunc myFunctionWithPointers(s *string){ fmt.Println(s, *s) }func main() { myOtherString := &quotanother message here&quot myFunctionWithPointers(&myOtherString) }خوب ایندفعه ما یکسری کارکتر جدید و عجیب داریم که اضافه شدن مثل * و &در داخل () main علامت & داره به زبان گو اعلام میکنه که این یک آدرسی هستش از یک متغیر، حالا گولنگ میاد میره دیتا تایپ و جایی که این مقدار در مموری قرار گرفته پیدا میکنه و نهایتا آدرس ارسال میشه به فانکشندر تابع myFunctionWithPointers علامت ستاره (*) داره به گو میگه که من دارم یک آدرس دریافت میکنم با دیتا تایپ رشته یا string. چیزی که مهمه اینکه پوینترها به تایپ نیاز دارند، پوینتر به یک عدد، پوینتر به یک رشته و یا پوینتر به یک slice و و و بعد از اینکه دیتاتایپ مشخص شد، اون در بدنه تابع ما داریم به زبان گو میگیم که s یک پوینتر است و مقداری که در اون جا ذخیره شده برای ما برمیگردهخوب حالا بریم سراغ structure type myStructure struct { Name string } func main() { structure := myStructure{Name: &quotMyName&quot} structureFunction(&structure) } func structureFunction(e *myStructure){ fmt.Println(e, *e, e.Name, (e).Name) }اولا ما مقدار استراکچر داریم پرینت میکنیم اما با مقدار & میبینیم که نشون داده میشه خوب این یعنی اینکه، یک پوینتر است به استراکچر با آن محتواوقتی که ما از ستاره استفاده میکنیم،‌ما داریم میگیم که مقدارها گرفته بشن بنابراین {MyName} که یک استراکچر فرمت شده است را دریافت میکنیموقتیکه شما دسترسی پیدا میکنی به یک فیلد از پوینتر استارکچر، گو میاد دقیقا به شما مقدار برمیگردونه. مورد آخر هم دقیقا داره همون کار انجام میده که دیگه بهش نیازی نیست و همون روش قبل کفایت میکنهحالا فرض کنین ما میخوایم متغیر آپدیت کنیم داخل تابعfunc main() { sliceValues := []string{&quota&quot,&quotb&quot, &quotc&quot} appendToSlice(sliceValues) fmt.Println(sliceValues) } func appendToSlice(c []string){ fmt.Println(c) c = append(c, &quotd&quot) }چی شده !!؟عدم استفاده از این دوستمون پوینتر نتیجش شد اینکه آپدیتی شکل نگرفت چون خارج از تابع بوده!func main() { sliceValues := []string{&quota&quot,&quotb&quot, &quotc&quot} appendToSliceWithPointer(&sliceValues) fmt.Println(sliceValues) } func appendToSliceWithPointer(c *[]string){ fmt.Println(*c) *c = append(*c, &quotd&quot) }خوب بنظر ساده میاد،‌حالا چه اتفاقی می افته اگر ما maps داشته باشیم !؟func main() { myMap := map[string]int{ &quota&quot: 1, &quotb&quot: 2, &quotc&quot: 3, } maps(myMap) fmt.Println(myMap) } func maps(c map[string]int){ c[&quotd&quot] = 4 fmt.Println(c) }توی این مثال، تغییر در map رفتارش متفاوت بود !آپدیت شدش ! داخل فانکشن به مقدار original map !‌ مگه داریم‌!؟چرا چون که خود maps در واقع پونتر هستند و در نتیجه ما نیازی نداریم کار خاصی با map ها بکنیمیه چیز کوچولو دیگه هم هست که باید بهش توجه کرد حدس بزنین ! تابع ها هم همچنین پوینتر هستند !func main() { f := &myFunc } func myFunc(){ }خوب این کار نمیکنه از اونجایی که myFunc خودش پوینتر هستش !حالا از اونجایی که پوینتر عملا یک دیتاتایپ هستش، ورودی های تابع رفتار مشابهی دارندtype myStructure struct { Name string } func (ms myStructure) noPointer(){ ms.Name = &quotxxxx&quot fmt.Println(ms.Name) } func (ms *myStructure) withPointer(){ ms.Name = &quotyyyy&quot fmt.Println(ms.Name) } func main() { theStructure := myStructure{Name: &quotMyName&quot} theStructure.noPointer() fmt.Println(theStructure.Name) theStructure.withPointer() fmt.Println(theStructure.Name) }وقتیکه ورودی به عنوان یک structure استفاده میشه، مقدار تاثیری روش گذاشته نمیشه خارج از اون تابعوقتیکه ورودی مدیریت میشه به عنوان یک پوینتر، مقدار رفتارش همونی میشه که ما انتظار داریم اما اگر شما بخواین متد مستقیما صدا بزنین:func (ms myStructure) noPointer(){ fmt.Println(&quotNot a pointer&quot) } func (ms *myStructure) withPointer(){ fmt.Println(&quota pointer&quot) } func main() { myStructure{}.noPointer() myStructure{}.withPointer() }دومین تابع یعنی withPointer خیلی ساده اجرا نمیشه، withPointer یک ورودی لینک شده هستش به pointer to myStructure دیتا تایپ و {} myStructure یک instance از myStructure ما میتونیم درستش کنیم با instancing structure و بعدش اضافه کردن یک پوینتر به اونtype myStructure struct { Name string } func (ms myStructure) noPointer(){ fmt.Println(&quotNot a pointer&quot) } func (ms *myStructure) withPointer(){ fmt.Println(&quota pointer&quot) } func main() { myStructure{}.noPointer() ms := &myStructure{} ms.withPointer() }خوب این تمام چیزیه که شما نیاز دارین بهش برای شروع کار با پوینتر ها :)سعی میکنم یه مقاله دیگه پیدا کنم یکم مباحث advance تری بگه. حالا برای شروع همچین بد هم نیستش :)مرسی