نسخه زبان go1.26 (۱۲ ژانویهٔ ۲۰۲۶)
فهرست مطالب
- مقدمه
- نمادگذاری
- نمایش کد منبع
- نویسهها
- حروف و رقمها
- عناصر واژگانی
- توضیحات
- توکنها
- نقطهویرگولها
- شناسهها
- کلمات کلیدی
- عملگرها و نشانههای سجاوندی
- لیترالهای عدد صحیح
- لیترالهای ممیز شناور
- لیترالهای موهومی
- لیترالهای rune
- لیترالهای رشتهای
- ثابتها
- متغیرها
- نوعها
- نوعهای بولی
- نوعهای عددی
- نوعهای رشتهای
- نوعهای آرایهای
- نوعهای slice
- نوعهای struct
- نوعهای اشارهگر
- نوعهای تابع
- نوعهای interface
- interfaceهای پایه
- interfaceهای تعبیهشده
- interfaceهای عمومی
- پیادهسازی یک interface
- نوعهای map
- نوعهای channel
- ویژگیهای نوعها و مقدارها
- بازنمایی مقدارها
- نوعهای زیربنایی
- همانیِ نوع
- قابلیت انتساب
- قابلنمایش بودن
- مجموعههای متد
- بلوکها
- اعلانها و دامنه
- دامنههای برچسب
- شناسه خالی
- شناسههای ازپیشاعلانشده
- شناسههای صادرشده
- یکتایی شناسهها
- اعلانهای ثابت
- iota
- اعلانهای نوع
- اعلانهای alias
- تعریفهای نوع
- اعلانهای پارامتر نوع
- محدودیتهای نوع
- برآورده کردن یک محدودیت نوع
- اعلانهای متغیر
- اعلانهای کوتاه متغیر
- اعلانهای تابع
- اعلانهای متد
- عبارتها
- عملوندها
- شناسههای واجد شرایط
- لیترالهای مرکب
- لیترالهای تابع
- عبارتهای اولیه
- انتخابگرها
- عبارتهای متد
- مقدارهای متد
- عبارتهای اندیس
- عبارتهای slice
- تصدیقهای نوع
- فراخوانیها
- ارسال آرگومانها به پارامترهای...
- نمونهسازیها
- استنتاج نوع
- یکسانسازی نوع
- عملگرها
- اولویت عملگرها
- عملگرهای حسابی
- عملگرهای عدد صحیح
- سرریز عدد صحیح
- عملگرهای ممیز شناور
- الحاق رشته
- عملگرهای مقایسهای
- عملگرهای منطقی
- عملگرهای آدرس
- عملگر دریافت
- تبدیلها
- تبدیل به نوع رشتهای و از آن
- تبدیل از slice به آرایه یا اشارهگر آرایه
- عبارتهای ثابت
- ترتیب ارزیابی
- دستورها
- دستورهای خاتمهدهنده
- دستورهای خالی
- دستورهای برچسبدار
- دستورهای عبارتی
- دستورهای ارسال
- دستورهای IncDec
- دستورهای انتساب
- دستورهای if
- دستورهای switch
- switchهای عبارتی
- switchهای نوع
- دستورهای for
- دستورهای for با یک شرط
- دستورهای for با بند for
- دستورهای for با بند range
- دستورهای go
- دستورهای select
- دستورهای return
- دستورهای break
- دستورهای continue
- دستورهای goto
- دستورهای fallthrough
- دستورهای defer
- توابع داخلی
- افزودن به sliceها و کپی کردن آنها
- clear
- close
- دستکاری اعداد مختلط
- حذف عناصر map
- طول و ظرفیت
- ساختن sliceها، mapها و channelها
- min و max
- تخصیص
- مدیریت panicها
- راهاندازی اولیه
- بستهها
- سازماندهی فایل منبع
- بند package
- اعلانهای import
- یک بسته نمونه
- مقداردهی اولیه و اجرای برنامه
- مقدار صفر
- مقداردهی اولیه بسته
- مقداردهی اولیه برنامه
- اجرای برنامه
- خطاها
- panicهای زمان اجرا
- ملاحظات سیستمی
- بسته unsafe
- تضمینهای اندازه و همترازی
- پیوست
- نسخههای زبان
- Go 1.9
- Go 1.13
- Go 1.14
- Go 1.17
- Go 1.18
- Go 1.20
- Go 1.21
- Go 1.22
- Go 1.23
- Go 1.24
- قواعد یکسانسازی نوع
این راهنمای مرجع برای زبان برنامهنویسی Go است. برای اطلاعات بیشتر و سایر اسناد، به go.dev مراجعه کنید.
گو یک زبان همه منظوره است که با در نظر گرفتن برنامهنویسی سیستمی طراحی شده است. این زبان به شدت تایپ شده و دارای قابلیت جمعآوری زباله است و پشتیبانی صریحی از برنامهنویسی همزمان دارد. برنامهها از بستههایی ساخته میشوند که ویژگیهای آنها امکان مدیریت کارآمد وابستگیها را فراهم میکند.
سینتکس آن فشرده و ساده برای تجزیه و تحلیل است و امکان تجزیه و تحلیل آسان توسط ابزارهای خودکار مانند محیطهای توسعه یکپارچه را فراهم میکند.
این سینتکس با استفاده از گونهای از فرم توسعهیافتهی بکوس-نائور (EBNF) مشخص شده است:
Syntax = { Production } .
Production = production_name "=" [ Expression ] "." .
Expression = Term { "|" Term } .
Term = Factor { Factor } .
Factor = production_name | token [ "…" token ] | Group | Option | Repetition .
Group = "(" Expression ")" .
Option = "[" Expression "]" .
Repetition = "{" Expression "}" .
محصولات، عباراتی هستند که از اصطلاحات و عملگرهای زیر، به ترتیب اولویت صعودی، ساخته شدهاند:
| alternation
() grouping
[] option (0 or 1 times)
{} repetition (0 to n times)
نامهای تولیدی با حروف کوچک برای شناسایی توکنهای لغوی (پایانی) استفاده میشوند. توکنهای غیرپایانی به صورت CamelCase هستند. توکنهای لغوی در داخل علامت نقل قول دوتایی ""یا علامت نقل قول معکوس قرار میگیرند ``.
این فرم، a … b مجموعهای از کاراکترها را از a طریق b به عنوان جایگزین نشان میدهد. حذف افقی …همچنین در جای دیگری از مشخصات برای نشان دادن غیررسمی انواع شمارشها یا قطعه کدهایی که بیشتر مشخص نشدهاند، استفاده میشود. این کاراکتر …(برخلاف سه کاراکتر...) نشانهای از زبان Go نیست.
پیوندی به شکل [Go 1.xx] نشان میدهد که یک ویژگی زبان توصیفشده (یا جنبهای از آن) با نسخه زبان ۱.xx تغییر یافته یا اضافه شده است و بنابراین حداقل به آن نسخه زبان برای ساخت نیاز دارد. برای جزئیات، به بخش پیوند داده شده در پیوست مراجعه کنید.
کد منبع، متنی یونیکد است که با UTF-8 کدگذاری شده است. متن استانداردسازی نشده است، بنابراین یک نقطه کد با لهجه با همان کاراکتر ساخته شده از ترکیب یک لهجه و یک حرف متمایز است؛ این دو به عنوان دو نقطه کد در نظر گرفته میشوند. برای سادگی، این سند از کاراکتر اصطلاح نامشخص برای اشاره به یک نقطه کد یونیکد در متن منبع استفاده خواهد کرد.
هر نقطه کد متمایز است؛ برای مثال، حروف بزرگ و کوچک کاراکترهای متفاوتی هستند.
محدودیت پیادهسازی: برای سازگاری با سایر ابزارها، یک کامپایلر ممکن است کاراکتر NUL (U+0000) را در متن منبع مجاز نداند.
محدودیت پیادهسازی: برای سازگاری با سایر ابزارها، کامپایلر میتواند علامت ترتیب بایت کدگذاری شده با UTF-8 (U+FEFF) را در صورتی که اولین نقطه کد یونیکد در متن منبع باشد، نادیده بگیرد. علامت ترتیب بایت ممکن است در هر جای دیگری از متن منبع مجاز نباشد.
اصطلاحات زیر برای نشان دادن دستههای خاص کاراکترهای یونیکد استفاده میشوند:
newline = /* the Unicode code point U+000A */ .
unicode_char = /* an arbitrary Unicode code point except newline */ .
unicode_letter = /* a Unicode code point categorized as "Letter" */ .
unicode_digit = /* a Unicode code point categorized as "Number, decimal digit" */ .در استاندارد یونیکد ۸.۰، بخش ۴.۵ «دستهبندی عمومی» مجموعهای از دستههای کاراکتر را تعریف میکند. زبان برنامهنویسی Go (Go) تمام کاراکترهای موجود در هر یک از دستههای حروف Lu، Ll، Lt، Lm یا Lo را به عنوان حروف یونیکد و کاراکترهای موجود در دسته اعداد Nd را به عنوان ارقام یونیکد در نظر میگیرد.
کاراکتر زیرخط _(U+005F) یک حرف کوچک محسوب میشود.
letter = unicode_letter | "_" .
decimal_digit = "0" … "9" .
binary_digit = "0" | "1" .
octal_digit = "0" … "7" .
hex_digit = "0" … "9" | "A" … "F" | "a" … "f" .توضیحات به عنوان مستندات برنامه عمل میکنند. دو شکل وجود دارد:
یک کامنت نمیتواند داخل یک rune یا string literal یا داخل یک کامنت شروع شود. یک کامنت عمومی که حاوی هیچ خط جدیدی نباشد، مانند یک فاصله عمل میکند. هر کامنت دیگری مانند یک خط جدید عمل میکند.
توکنها واژگان زبان Go را تشکیل میدهند. چهار کلاس وجود دارد: شناسهها، کلمات کلیدی، عملگرها و علائم نگارشی و حروف. فضای خالی که از فاصلهها (U+0020)، تبهای افقی (U+0009)، علامتهای بازگشتی (U+000D) و خطوط جدید (U+000A) تشکیل میشود، نادیده گرفته میشود، مگر اینکه توکنهایی را که در غیر این صورت در یک توکن واحد ترکیب میشوند، از هم جدا کند. همچنین، یک خط جدید یا انتهای فایل ممکن است باعث درج یک نقطه ویرگول شود. هنگام شکستن ورودی به توکنها، توکن بعدی طولانیترین توالی کاراکترهایی است که یک توکن معتبر را تشکیل میدهند.
وقتی ورودی به توکنها تقسیم میشود، اگر آن توکن وجود داشته باشد، بلافاصله پس از آخرین توکن یک خط، یک نقطهویرگول به طور خودکار در جریان توکنها قرار میگیرد. یک شناسه یک عدد صحیح، ممیز شناور، عدد موهومی، عدد رون یا رشتهی تحتاللفظی یکی از کلمات کلیدی break، continue، fallthrough، یا return برای انعکاس کاربرد اصطلاحی، نمونههای کد در این سند با استفاده از این قوانین، نقطهویرگولها را حذف میکنند.
شناسهها، موجودیتهای برنامه مانند متغیرها و انواع را نامگذاری میکنند. یک شناسه، دنبالهای از یک یا چند حرف و رقم است. اولین کاراکتر در یک شناسه باید یک حرف باشد.
identifier = letter { letter | unicode_digit } .a
_x9
ThisVariableIsExported
αβ
الف آلفا برخی از شناسهها از پیش تعریف شدهاند.
کلمات کلیدی زیر رزرو شده هستند و نمیتوان از آنها به عنوان شناسه استفاده کرد.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
ادامه برای وارد کردن مقدار return var
توالیهای کاراکتری زیر نشان دهنده عملگرها (از جمله عملگرهای انتساب) و علائم نگارشی هستند [Go 1.18]:
+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^= ~
یک لیترال عدد صحیح، دنبالهای از ارقام است که یک ثابت عدد صحیح را نشان میدهد. یک پیشوند اختیاری، مبنای غیر اعشاری را تعیین میکند: 0b یا 0B برای دودویی، 0، 0o، یا 0O برای اکتال، و 0x یا 0X برای هگزادسیمال [به بخش ۱.۱۳ بروید]. یک عدد واحد، 0 صفر اعشاری در نظر گرفته میشود. در لیترالهای هگزادسیمال، حروف athrough f و Athrough F نشاندهنده مقادیر ۱۰ تا ۱۵ هستند.
برای خوانایی بیشتر، ممکن است یک کاراکتر زیرخط _ بعد از یک پیشوند پایه یا بین ارقام متوالی ظاهر شود؛ چنین زیرخطهایی مقدار لفظ را تغییر نمیدهند.
int_lit = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit = "0" ( "x" | "X" ) [ "_" ] hex_digits .
decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits = binary_digit { [ "_" ] binary_digit } .
octal_digits = octal_digit { [ "_" ] octal_digit } .
hex_digits = hex_digit { [ "_" ] hex_digit } .42
4_2
0600
0_600
0o600
0O600 // second character is capital letter 'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727
_42 // an identifier, not an integer literal
42_ // invalid: _ must separate successive digits
4__2 // invalid: only one _ at a time
0_xBadFace // invalid: _ must separate successive digits
یک لیترال ممیز شناور، نمایش اعشاری یا هگزادسیمال یک ثابت ممیز شناور است.
یک لیترال اعشاری اعشاری شامل یک بخش صحیح (رقم اعشاری)، یک نقطه اعشاری، یک بخش کسری (رقم اعشاری) و یک بخش توان (e یا E به دنبال آن یک علامت اختیاری و ارقام اعشاری) است. یکی از بخش صحیح یا بخش کسری را میتوان حذف کرد؛ یکی از نقطه اعشاری یا بخش توان را میتوان حذف کرد. یک مقدار توان exp، مانتیس (بخش صحیح و کسری) را به اندازه 10 exp مقیاس میدهد.
یک لیترال ممیز شناور هگزادسیمال شامل یک پیشوند 0x یا 0X، یک بخش صحیح (ارقام هگزادسیمال)، یک نقطه مبنا، یک بخش کسری (ارقام هگزادسیمال) و یک بخش توان (p یا P به دنبال آن یک علامت اختیاری و ارقام اعشاری) است. یکی از بخش صحیح یا بخش کسری را میتوان حذف کرد؛ نقطه مبنا را نیز میتوان حذف کرد، اما بخش توان الزامی است. (این سینتکس با سینتکس ارائه شده در IEEE 754-2008 §5.12.3 مطابقت دارد.) یک مقدار توان exp مانتیس (بخش صحیح و کسری) را به اندازه ۲ exp [Go 1.13] مقیاسبندی میکند.
برای خوانایی بیشتر، ممکن است یک کاراکتر زیرخط _ بعد از یک پیشوند پایه یا بین ارقام متوالی ظاهر شود؛ چنین زیرخطهایی مقدار تحتاللفظی را تغییر نمیدهند.
float_lit = decimal_float_lit | hex_float_lit .
decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
decimal_digits decimal_exponent |
"." decimal_digits [ decimal_exponent ] .
decimal_exponent = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .
hex_float_lit = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
hex_mantissa = [ "_" ] hex_digits "." [ hex_digits ] |
[ "_" ] hex_digits |
"." hex_digits .
hex_exponent = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .0.
72.40
072.40 // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5. // == 15.0
0.15e+0_2 // == 15.0
0x1p-2 // == 0.25
0x2.p10 // == 2048.0
0x1.Fp+0 // == 1.9375
0X.8p-0 // == 0.5
0X_1FFFP-16 // == 0.1249847412109375
0x15e-2 // == 0x15e - 2 (integer subtraction)
0x.p1 // invalid: mantissa has no digits
1p-2 // invalid: p exponent requires hexadecimal mantissa
0x1.5e-2 // invalid: hexadecimal mantissa requires p exponent
1_.5 // invalid: _ must separate successive digits
1._5 // invalid: _ must separate successive digits
1.5_e1 // invalid: _ must separate successive digits
1.5e_1 // invalid: _ must separate successive digits
1.5e1_ // invalid: _ must separate successive digits
یک لیترال خیالی، بخش خیالی یک ثابت مختلط را نشان میدهد. این لیترال شامل یک عدد صحیح یا عدد اعشاری است که به دنبال آن یک حرف کوچک میآید i. مقدار یک لیترال خیالی، مقدار عدد صحیح یا عدد اعشاری مربوطه است که در واحد خیالی i ضرب شده است [Go 1.13]
imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .برای سازگاری رو به عقب، بخش صحیح یک لیترال موهومی که کاملاً از ارقام اعشاری (و احتمالاً زیرخط) تشکیل شده باشد، حتی اگر با یک شروع شود، یک عدد صحیح اعشاری در نظر گرفته میشود 0.
0i
0123i // == 123i for backward-compatibility
0o123i // == 0o123 * 1i == 83i
0xabci // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i // == 0x1p-2 * 1i == 0.25i
یک رون لیترال نشان دهنده یک ثابت رون است، یک مقدار صحیح که یک نقطه کد یونیکد را مشخص میکند. یک رون لیترال به صورت یک یا چند کاراکتر محصور در نقل قولهای تکی، مانند 'x'یا، بیان میشود '\n'. در داخل نقل قولها، هر کاراکتری ممکن است به جز خط جدید و نقل قول تکی بدون پوشش ظاهر شود. یک کاراکتر نقل قول تکی نشان دهنده مقدار یونیکد خود کاراکتر است، در حالی که توالیهای چند کاراکتری که با یک بک اسلش شروع میشوند، مقادیر را در قالبهای مختلف کدگذاری میکنند.
سادهترین شکل، کاراکتر منفرد درون علامت نقل قول را نشان میدهد؛ از آنجایی که متن منبع Go کاراکترهای یونیکد کدگذاری شده در UTF-8 است، چندین بایت کدگذاری شده با UTF-8 میتوانند یک مقدار صحیح واحد را نشان دهند. برای مثال، متغیر تحتاللفظی 'a'یک بایت را نگه میدارد که نشاندهنده یک متغیر تحتاللفظی a، Unicode U+0061, value است 0x61، در حالی که 'ä'دو بایت (0xc3 0xa4) را نگه میدارد که نشاندهنده یک متغیر تحتاللفظی a، -dieresis, U+00E4, value است 0xe4.
چندین کاراکتر بکاسلش (\) به مقادیر دلخواه اجازه میدهد تا به صورت متن ASCII کدگذاری شوند. چهار روش برای نمایش مقدار عدد صحیح به عنوان یک ثابت عددی وجود دارد: \x به دنبال آن دقیقاً دو رقم هگزادسیمال؛ \u به دنبال آن دقیقاً چهار رقم هگزادسیمال؛ \U به دنبال آن دقیقاً هشت رقم هگزادسیمال و یک بکاسلش ساده \و به دنبال آن دقیقاً سه رقم اکتال. در هر مورد، مقدار لیترال، مقداری است که توسط ارقام در مبنای مربوطه نمایش داده میشود.
اگرچه همه این نمایشها منجر به یک عدد صحیح میشوند، اما محدودههای معتبر متفاوتی دارند. escape های هشتتایی باید مقداری بین ۰ تا ۲۵۵ را شامل شوند. escape های هگزادسیمال از نظر ساختاری این شرط را برآورده میکنند. escape های \u و و \U نشاندهنده نقاط کد یونیکد هستند، بنابراین برخی از مقادیر در آنها غیرمجاز هستند، به ویژه مقادیر بالاتر 0x10FFFF و نیمههای جایگزین.
بعد از یک بکاسلش، کاراکترهای escape خاص، مقادیر خاصی را نشان میدهند:
\a U+0007 alert or bell
\b U+0008 backspace
\f U+000C form feed
\n U+000A line feed or newline
\r U+000D carriage return
\t U+0009 horizontal tab
\v U+000B vertical tab
\\ U+005C backslash
\' U+0027 single quote (valid escape only within rune literals)
\" U+0022 double quote (valid escape only within string literals)
هشدار یا زنگ \a U+0007 \b U+0008 بکاسپیس \f خوراک فرم U+000C \n U+000A تغذیه خط یا خط جدید \r U+000D کالسکه برگشتی تب افقی U+0009 زبانه عمودی U+000B \\ U+005C بک اسلش \' U+0027 تککوتیشن (فقط در حروف الفبای رونها، escape معتبر است) یک کاراکتر ناشناخته پس از بک اسلش در یک رون به معنای واقعی کلمه غیرقانونی است.
rune_lit = "'" ( unicode_value | byte_value ) "'" .
unicode_value = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value = `\` "x" hex_digit hex_digit .
little_u_value = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value = `\` "U" hex_digit hex_digit hex_digit hex_digit
hex_digit hex_digit hex_digit hex_digit .
escaped_char = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\'' // rune literal containing single quote character
'aa' // illegal: too many characters
'\k' // illegal: k is not recognized after a backslash
'\xa' // illegal: too few hexadecimal digits
'\0' // illegal: too few octal digits
'\400' // illegal: octal value over 255
'\uDFFF' // illegal: surrogate half
'\U00110000' // illegal: invalid Unicode code point
شش رقمی رقم شش رقمی.
یک رشتهی لیترال (string literal) نشان دهندهی یک ثابت رشتهای است که از به هم پیوستن دنبالهای از کاراکترها به دست میآید. دو شکل وجود دارد: رشتهی لیترال خام (raw string literal) و رشتهی لیترال تفسیر شده (interpreted string literal).
رشتههای خام، توالی کاراکترهایی هستند که بین علامت نقل قول معکوس قرار میگیرند، مانند `foo`. درون نقل قولها، هر کاراکتری ممکن است ظاهر شود به جز نقل قول معکوس. مقدار یک رشته خام، رشتهای است که از کاراکترهای تفسیر نشده (به طور ضمنی UTF-8 کدگذاری شده) بین نقل قولها تشکیل شده است؛ به طور خاص، بکاسلشها معنی خاصی ندارند و رشته ممکن است حاوی خط جدید باشد. کاراکترهای بازگشتی ('\r') درون رشتههای خام، از مقدار رشته خام حذف میشوند.
رشتههای تفسیر شده، توالیهای کاراکتری بین علامت نقل قول دوتایی هستند، مانند "bar". درون علامت نقل قول، هر کاراکتری میتواند ظاهر شود، به جز کاراکتر خط جدید و علامت نقل قول دوتایی بدون اسکیپ. متن بین علامت نقل قول، مقدار رشته را تشکیل میدهد و کاراکترهای اسکیپ بکاسلش مانند رشتههای Rune تفسیر میشوند (به جز اینکه کدام یک \'غیرقانونی و \"کدام یک قانونی است)، با همان محدودیتها. اسکیپهای سه رقمی هشتایی (\nnn) و شش رقمی شانزدهتایی (\xnn) نشان دهنده بایتهای رشته حاصل هستند؛ همه اسکیپهای دیگر نشان دهنده کدگذاری (احتمالاً چند بایتی) UTF-8 کاراکترهای منفرد هستند. بنابراین درون یک رشته، \377 و \xFF یک بایت با مقدار 0xFF=255 را نشان میدهند، در حالی که ÿ، \u00FF، \U000000FF و \xc3\xbf دو بایت 0xc3 0xbf کدگذاری UTF-8 کاراکتر U+00FF را نشان میدهند.
string_lit = raw_string_lit | interpreted_string_lit .
raw_string_lit = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .`abc` // same as "abc"
`\n
\n` // same as "\\n\n\\n"
"\n"
"\"" // same as `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800" // illegal: surrogate half
"\U00110000" // illegal: invalid Unicode code point
این مثالها همگی نشاندهندهی یک رشتهی یکسان هستند:
"日本語" // UTF-8 input text
`日本語` // UTF-8 input text as a raw literal
"\u65e5\u672c\u8a9e" // the explicit Unicode code points
"\U000065e5\U0000672c\U00008a9e" // the explicit Unicode code points
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // the explicit UTF-8 bytes
اگر کد منبع، یک کاراکتر را به صورت دو نقطه کد نمایش دهد، مانند یک فرم ترکیبی شامل یک لهجه و یک حرف، اگر در یک رون لیترال قرار گیرد (یک نقطه کد واحد نیست)، نتیجه یک خطا خواهد بود و اگر در یک رشته لیترال قرار گیرد، به صورت دو نقطه کد ظاهر میشود.
ثابتهای بولی، ثابتهای رون، ثابتهای عدد صحیح، ثابتهای ممیز شناور، ثابتهای مختلط و ثابتهای رشتهای وجود دارند. ثابتهای رون، عدد صحیح، ممیز شناور و مختلط در مجموع ثابتهای عددی نامیده میشوند.
یک مقدار ثابت توسط یک rune، integer، floating-point، imagine یا string literal، یک شناسه که نشان دهنده یک ثابت است، یک عبارت ثابت، یک تبدیل با نتیجه ای که یک ثابت است، یا مقدار نتیجه برخی از توابع داخلی مانند min یا max اعمال شده بر آرگومان های ثابت، unsafe.Sizeof اعمال شده بر مقادیر خاص، cap یا len اعمال شده بر برخی عبارات، real و imag اعمال شده بر یک ثابت پیچیده و اعمال شده بر ثابت های عددی نمایش داده می شود. مقادیر حقیقی بولی توسط ثابت های از پیش تعریف شده و complex نمایش داده می شوند. شناسه از پیش تعریف شده iota نشان دهنده یک ثابت عدد صحیح است.truefalse
به طور کلی، ثابتهای مختلط نوعی از بیان ثابت هستند و در آن بخش مورد بحث قرار میگیرند.
ثابتهای عددی مقادیر دقیقی با دقت دلخواه را نشان میدهند و سرریز نمیشوند. در نتیجه، هیچ ثابتی وجود ندارد که مقادیر صفر منفی، بینهایت و غیرعددی IEEE 754 را نشان دهد.
ثابتها میتوانند نوعدار یا بدون نوع باشند. ثابتهای تحتاللفظی، true، false، iota و برخی از عبارات ثابت که فقط شامل عملوندهای ثابت بدون نوع هستند، بدون نوع هستند.
یک ثابت میتواند به صورت صریح توسط اعلان یا تبدیل ثابت، یا به صورت ضمنی هنگام استفاده در اعلان متغیر یا دستور انتساب یا به عنوان یک عملوند در یک عبارت، نوع داده شود. اگر مقدار ثابت را نتوان به عنوان مقداری از نوع مربوطه نمایش داد، خطا رخ میدهد. اگر نوع، یک پارامتر نوع باشد، ثابت به یک مقدار غیر ثابت از پارامتر نوع تبدیل میشود.
محدودیت پیادهسازی: اگرچه ثابتهای عددی در زبان دقت دلخواهی دارند، اما یک کامپایلر میتواند آنها را با استفاده از یک نمایش داخلی با دقت محدود پیادهسازی کند. با این اوصاف، هر پیادهسازی باید:
ثابتهای ممیز شناور، شامل بخشهایی از یک ثابت مختلط، را با مانتیس حداقل ۲۵۶ بیتی و توان دودویی علامتدار حداقل ۱۶ بیتی نمایش دهید. اگر به دلیل محدودیتهای دقت، نتوانید یک عدد ممیز شناور یا ثابت مختلط را نمایش دهید، به نزدیکترین ثابت قابل نمایش گرد کنید. این الزامات هم برای ثابتهای تحتاللفظی و هم برای نتیجه ارزیابی عبارات ثابت اعمال میشود.
متغیر، مکانی برای ذخیره سازی یک مقدار است. مجموعه مقادیر مجاز توسط نوع متغیر تعیین میشود.
یک اعلان متغیر یا برای پارامترها و نتایج تابع، امضای اعلان تابع یا تابع تحتاللفظی، فضای ذخیرهسازی را برای یک متغیر نامگذاری شده رزرو میکند. فراخوانی تابع داخلی new یا گرفتن آدرس یک تابع تحتاللفظی مرکب، فضای ذخیرهسازی را برای یک متغیر در زمان اجرا اختصاص میدهد. چنین متغیر ناشناسی از طریق یک اشارهگر غیرمستقیم (احتمالاً ضمنی) ارجاع داده میشود.
متغیرهای ساختاریافته از نوع آرایه، slice و ساختار، عناصر و فیلدهایی دارند که میتوان به صورت جداگانه به آنها آدرسدهی کرد. هر یک از این عناصر مانند یک متغیر عمل میکنند.
نوع استاتیک (یا فقط نوع) یک متغیر، نوعی است که در اعلان آن، نوع ارائه شده در فراخوانی new یا لیترال مرکب، یا نوع یک عنصر از یک متغیر ساختاریافته، ذکر شده است. متغیرهایی از نوع رابط همچنین یک نوع پویای متمایز دارند که نوع (غیر رابط) مقداری است که در زمان اجرا به متغیر اختصاص داده میشود (مگر اینکه مقدار، شناسه از پیش اعلام شده باشد nil که هیچ نوعی ندارد). نوع پویا ممکن است در طول اجرا تغییر کند، اما مقادیر ذخیره شده در متغیرهای رابط همیشه به نوع استاتیک متغیر قابل انتساب هستند.
var x interface{} // x is nil and has static type interface{}
var v *T // v has value nil, static type *T
x = 42 // x has value 42 and dynamic type int
x = v // x has value (*T)(nil) and dynamic type *Tمقدار یک متغیر با ارجاع به متغیر در یک عبارت بازیابی میشود؛ این مقدار، جدیدترین مقداری است که به متغیر اختصاص داده شده است. اگر هنوز به یک متغیر مقداری اختصاص داده نشده باشد، مقدار آن برابر با مقدار صفر برای نوع آن است.
یک نوع، مجموعهای از مقادیر را به همراه عملیات و متدهای مختص به آن مقادیر تعیین میکند. یک نوع، در صورت داشتن نام نوع، میتواند با نام آن مشخص شود که در صورت عمومی بودن نوع، باید با آرگومانهای نوع دنبال شود. همچنین میتوان یک نوع را با استفاده از یک نوع تحتاللفظی مشخص کرد که یک نوع را از انواع موجود تشکیل میدهد.
Type = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
TypeName = identifier | QualifiedIdent .
TypeArgs = "[" TypeList [ "," ] "]" .
TypeList = Type { "," Type } .
TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
SliceType | MapType | ChannelType .این زبان نامهای نوع خاصی را از پیش اعلام میکند. برخی دیگر با اعلانهای نوع یا لیستهای پارامترهای نوع معرفی میشوند. انواع مرکب - آرایه، ساختار، اشارهگر، تابع، رابط، slice، map و کانال - ممکن است با استفاده از لیترالهای نوع ساخته شوند.
انواع ازپیشاعلانشده، انواع تعریفشده و پارامترهای نوع، انواع نامگذاریشده نامیده میشوند. اگر نوع دادهشده در اعلان نام مستعار، یک نوع نامگذاریشده باشد، یک نام مستعار نشاندهنده یک نوع نامگذاریشده است.
یک نوع بولی (boolean) مجموعهای از مقادیر حقیقی بولی را نشان میدهد که با ثابتهای از پیش تعریف شده true و مشخص میشوند false. نوع بولی از پیش تعریف شده است bool. این یک نوع تعریف شده است.
یک نوع عدد صحیح، ممیز شناور یا مختلط به ترتیب مجموعهای از مقادیر عدد صحیح، ممیز شناور یا مختلط را نشان میدهد. آنها در مجموع انواع عددی نامیده میشوند. انواع عددی از پیش تعریف شده مستقل از معماری عبارتند از:
uint8 the set of all unsigned 8-bit integers (0 to 255)
uint16 the set of all unsigned 16-bit integers (0 to 65535)
uint32 the set of all unsigned 32-bit integers (0 to 4294967295)
uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)
int8 the set of all signed 8-bit integers (-128 to 127)
int16 the set of all signed 16-bit integers (-32768 to 32767)
int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
float32 the set of all IEEE 754 32-bit floating-point numbers
float64 the set of all IEEE 754 64-bit floating-point numbers
complex64 the set of all complex numbers with float32 real and imaginary parts
complex128 the set of all complex numbers with float64 real and imaginary parts
byte alias for uint8
rune alias for int32
uint8 مجموعه تمام اعداد صحیح ۸ بیتی بدون علامت (۰ تا ۲۵۵) uint16 مجموعه تمام اعداد صحیح ۱۶ بیتی بدون علامت (۰ تا ۶۵۵۳۵) uint32 مجموعه تمام اعداد صحیح ۳۲ بیتی بدون علامت (۰ تا ۴۲۹۴۹۶۷۲۹۵) uint64 مجموعه تمام اعداد صحیح ۶۴ بیتی بدون علامت (۰ تا ۱۸۴۴۶۷۴۴۰۷۳۷۰۹۵۵۱۶۱۵)
int8 مجموعه تمام اعداد صحیح ۸ بیتی علامتدار (۱۲۸- تا ۱۲۷) int16 مجموعه تمام اعداد صحیح ۱۶ بیتی علامتدار (-۳۲۷۶۸ تا ۳۲۷۶۷) int32 مجموعه تمام اعداد صحیح ۳۲ بیتی علامتدار (-۲۱۴۷۴۸۳۶۴۸ تا ۲۱۴۷۴۸۳۶۴۷) int64 مجموعه تمام اعداد صحیح ۶۴ بیتی علامتدار (-9223372036854775808 تا 9223372036854775807)
uint either 32 or 64 bits
int same size as uint
uintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value
float32 مجموعه تمام اعداد ممیز شناور ۳۲ بیتی IEEE 754 float64 مجموعه تمام اعداد ممیز شناور 64 بیتی IEEE 754
complex64 مجموعه تمام اعداد مختلط با بخشهای حقیقی و موهومی float32 complex128 مجموعه تمام اعداد مختلط با بخشهای حقیقی و موهومی float64
نام مستعار بایت برای uint8 نام مستعار rune برای int32 مقدار یک عدد صحیح n بیتی، n بیت عرض دارد و با استفاده از حساب مکمل دو نمایش داده میشود.
همچنین مجموعهای از انواع عدد صحیح از پیش تعریف شده با اندازههای خاص پیادهسازی وجود دارد:
uint یا ۳۲ یا ۶۴ بیتی uintptr یک عدد صحیح بدون علامت به اندازه کافی بزرگ برای ذخیره بیتهای تفسیر نشده یک مقدار اشارهگر برای جلوگیری از مشکلات مربوط به قابلیت حمل، همه انواع عددی، انواع تعریفشده هستند و بنابراین از هم متمایزند، به جز byte که یک نام مستعار برای uint8 و rune است که یک نام مستعار برای است int32. تبدیلهای صریح زمانی مورد نیاز است که انواع عددی مختلف در یک عبارت یا انتساب با هم ترکیب شوند. به عنوان مثال، int32 و int از یک نوع نیستند، حتی اگر در یک معماری خاص اندازه یکسانی داشته باشند.
یک نوع رشته، مجموعهای از مقادیر رشتهای را نشان میدهد. یک مقدار رشتهای، دنبالهای (احتمالاً خالی) از بایتها است. تعداد بایتها، طول رشته نامیده میشود و هرگز منفی نیست. رشتهها تغییرناپذیر هستند: پس از ایجاد، تغییر محتوای یک رشته غیرممکن است. نوع رشته از پیش تعریف شده string; است. این یک نوع تعریف شده است.
طول یک رشته را s میتوان با استفاده از تابع داخلی کشف کرد len. اگر رشته یک ثابت باشد، طول آن یک ثابت زمان کامپایل است. بایتهای یک رشته را میتوان با اندیسهای عدد صحیح از 0 تا 10 دسترسی داشت len(s)-1. گرفتن آدرس چنین عنصری غیرقانونی است؛ اگر بایت 'ام یک رشته s[i] باشد، نامعتبر است.i&s[i]
یک آرایه، دنبالهای شمارهگذاری شده از عناصر یک نوع واحد است که نوع عنصر نامیده میشود. تعداد عناصر، طول آرایه نامیده میشود و هرگز منفی نیست.
ArrayType = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .طول بخشی از نوع آرایه است؛ باید به یک ثابت غیر منفی که توسط مقداری از نوع قابل نمایش است، ارزیابی شود int. طول آرایه را a میتوان با استفاده از تابع داخلی کشف کرد len. عناصر را میتوان با اندیسهای عدد صحیح از 0 تا آدرسدهی کرد len(a)-1. انواع آرایه همیشه یک بعدی هستند، اما میتوانند برای تشکیل انواع چند بعدی ترکیب شوند.
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // same as [2]([2]([2]float64))یک نوع آرایه T نمیتواند عنصری از نوع داشته باشد T، یا از نوعی که T به عنوان یک جزء، به طور مستقیم یا غیرمستقیم، شامل شود، اگر آن نوعهای شامل فقط از نوع آرایه یا ساختار باشند.
// invalid array types
type (
T1 [10]T1 // element type of T1 is T1
T2 [10]struct{ f T2 } // T2 contains T2 as component of a struct
T3 [10]T4 // T3 contains T3 as component of a struct in T4
T4 struct{ f T3 } // T4 contains T4 as component of array T3 in a struct
)
// valid array types
type (
T5 [10]*T5 // T5 contains T5 as component of a pointer
T6 [10]func() T6 // T6 contains T6 as component of a function type
T7 [10]struct{ f []T7 } // T7 contains T7 as component of a slice in a struct
)یک slice، توصیفگری برای یک بخش پیوسته از یک آرایهی زیرین است و دسترسی به یک توالی شمارهگذاری شده از عناصر آن آرایه را فراهم میکند. نوع slice، مجموعهی تمام sliceهای آرایههای از نوع عنصر خود را نشان میدهد. تعداد عناصر، طول slice نامیده میشود و هرگز منفی نیست. مقدار یک slice بدون مقدار اولیه، است nil.
SliceType = "[" "]" ElementType .طول یک slice را s میتوان توسط تابع داخلی کشف کرد len؛ برخلاف آرایهها، طول slice ممکن است در طول اجرا تغییر کند. عناصر را میتوان با اندیسهای عدد صحیح از 0 تا آدرسدهی کرد len(s)-1. اندیس slice یک عنصر معین میتواند کمتر از اندیس همان عنصر در آرایه زیرین باشد.
یک slice، پس از مقداردهی اولیه، همیشه با یک آرایه زیرین که عناصر آن را در خود نگه میدارد، مرتبط است. بنابراین، یک slice، فضای ذخیرهسازی را با آرایه خود و با sliceهای دیگر همان آرایه به اشتراک میگذارد؛ در مقابل، آرایههای مجزا همیشه نشاندهنده فضای ذخیرهسازی مجزا هستند.
آرایهای که زیر یک slice قرار دارد، میتواند از انتهای slice فراتر رود. ظرفیت، معیاری از آن وسعت است: این مقدار برابر با مجموع طول slice و طول آرایه فراتر از slice است؛ میتوان با slice یک slice جدید از slice اصلی، slice ی به طول حداکثر آن ظرفیت ایجاد کرد. ظرفیت یک slice را a میتوان با استفاده از تابع داخلی کشف کرد cap(a).
یک مقدار slice جدید و مقداردهی اولیه شده برای یک نوع عنصر معین T میتواند با استفاده از تابع داخلی ساخته شود make، که یک نوع slice و پارامترهایی را که طول و به صورت اختیاری ظرفیت را مشخص میکنند، میگیرد. slice ی که با ایجاد میشود، make همیشه یک آرایه جدید و پنهان را اختصاص میدهد که مقدار slice برگشتی به آن اشاره دارد. یعنی اجرا کردن
make([]T, length, capacity)
همان slice ی را تولید میکند که تخصیص یک آرایه و slice آن ایجاد میکند، بنابراین این دو عبارت معادل هستند:
make([]int, 50, 100)
new([100]int)[0:50]
مانند آرایهها، sliceها همیشه یک بعدی هستند اما میتوانند برای ساخت اشیاء با ابعاد بالاتر ترکیب شوند. در آرایههایی از آرایهها، آرایههای داخلی، از نظر ساختار، همیشه طول یکسانی دارند. با این حال، در sliceهایی از sliceها (یا آرایههایی از sliceها)، طول داخلی ممکن است به صورت پویا تغییر کند. علاوه بر این، sliceهای داخلی باید به صورت جداگانه مقداردهی اولیه شوند.
یک ساختار (struct) دنباله ای از عناصر نامگذاری شده است که فیلد نامیده می شوند و هر کدام دارای یک نام و یک نوع هستند. نام فیلدها ممکن است به صورت صریح (IdentifierList) یا به صورت ضمنی (EmbeddedField) مشخص شوند. در یک ساختار، نام فیلدهای غیر خالی باید منحصر به فرد باشد.
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag = string_lit .// An empty struct.
struct {}
// A struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}فیلدی که با یک نوع تعریف شده باشد اما نام فیلد صریحی نداشته باشد، فیلد تعبیهشده نامیده میشود. یک فیلد تعبیهشده باید به عنوان نام نوع T یا به عنوان اشارهگری به نام نوع غیر رابط مشخص شود *T و T خودش نمیتواند یک نوع اشارهگر یا پارامتر نوع باشد. نام نوع نامشخص به عنوان نام فیلد عمل میکند.
// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
struct {
T1 // field name is T1
*T2 // field name is T2
P.T3 // field name is T3
*P.T4 // field name is T4
x, y int // field names are x and y
}اعلان زیر غیرقانونی است زیرا نام فیلدها باید در نوع ساختار منحصر به فرد باشند:
struct {
T // conflicts with embedded field *T and *P.T
*T // conflicts with embedded field T and *P.T
*P.T // conflicts with embedded field T and *T
}یک فیلد یا متد f از یک فیلد تعبیهشده در یک ساختار، promotedx نامیده میشود اگر یک انتخابگر قانونی باشد که آن فیلد یا متد را مشخص میکند.x.ff
فیلدهای ارتقا یافته مانند فیلدهای معمولی یک ساختار عمل میکنند، با این تفاوت که نمیتوان از آنها به عنوان نام فیلد در لیترالهای مرکب ساختار استفاده کرد.
با توجه به نوع ساختار S و نام نوع T، متدهای ارتقا یافته به صورت زیر در مجموعه متدهای ساختار قرار میگیرند:
اگر S شامل یک فیلد تعبیه شده باشد T، مجموعه متدهای و S هر *S دو شامل متدهای ارتقا یافته با receiver هستند T. مجموعه متدهای *S همچنین شامل متدهای ارتقا یافته با receiver است *T. اگر S شامل یک فیلد تعبیه شده باشد *T، مجموعه متدهای S و *S هر دو شامل متدهای ارتقا یافته با گیرنده T یا هستند *T. یک اعلان فیلد میتواند با یک برچسب رشتهای اختیاری دنبال شود که به یک ویژگی برای تمام فیلدهای موجود در اعلان فیلد مربوطه تبدیل میشود. یک رشته برچسب خالی معادل یک برچسب غایب است. برچسبها از طریق یک رابط بازتاب قابل مشاهده هستند و در شناسایی نوع ساختارها شرکت میکنند، اما در غیر این صورت نادیده گرفته میشوند.
یک نوع struct T نمیتواند شامل فیلدی از نوع T یا نوعی که T به عنوان یک جزء در بر میگیرد، به طور مستقیم یا غیرمستقیم باشد، اگر آن نوعهای شامل فقط از نوع آرایه یا struct باشند.
struct {
x, y float64 "" // an empty tag string is like an absent tag
name string "any string is permitted as a tag"
_ [4]byte "ceci n'est pas un champ de structure"
}
// A struct corresponding to a TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers;
// they follow the convention outlined by the reflect package.
struct {
microsec uint64 `protobuf:"1"`
serverIP6 uint64 `protobuf:"2"`
}// invalid struct types
type (
T1 struct{ T1 } // T1 contains a field of T1
T2 struct{ f [10]T2 } // T2 contains T2 as component of an array
T3 struct{ T4 } // T3 contains T3 as component of an array in struct T4
T4 struct{ f [10]T3 } // T4 contains T4 as component of struct T3 in an array
)
// valid struct types
type (
T5 struct{ f *T5 } // T5 contains T5 as component of a pointer
T6 struct{ f func() T6 } // T6 contains T6 as component of a function type
T7 struct{ f [10][]T7 } // T7 contains T7 as component of a slice in an array
)یک نوع اشارهگر، مجموعهی تمام اشارهگرها به متغیرهایی از یک نوع داده شده را نشان میدهد که نوع پایهی اشارهگر نامیده میشود. مقدار یک اشارهگر مقداردهی اولیه نشده، است nil.
PointerType = "*" BaseType .
BaseType = Type .*Point
*[4]int
یک نوع تابع، مجموعه تمام توابع با پارامتر و نوع نتیجه یکسان را نشان میدهد. مقدار یک متغیر مقداردهی اولیه نشده از نوع تابع، است nil.
FunctionType = "func" Signature .
Signature = Parameters [ Result ] .
Result = Parameters | Type .
Parameters = "(" [ ParameterList [ "," ] ] ")" .
ParameterList = ParameterDecl { "," ParameterDecl } .
ParameterDecl = [ IdentifierList ] [ "..." ] Type .در یک لیست از پارامترها یا نتایج، نامها (IdentifierList) یا باید همگی وجود داشته باشند یا همگی وجود نداشته باشند. در صورت وجود، هر نام نمایانگر یک مورد (پارامتر یا نتیجه) از نوع مشخص شده است و همه نامهای غیر خالی در امضا باید منحصر به فرد باشند. در صورت عدم وجود، هر نوع نمایانگر یک مورد از آن نوع است. لیستهای پارامتر و نتیجه همیشه با پرانتز نوشته میشوند، به جز اینکه اگر دقیقاً یک نتیجه بدون نام وجود داشته باشد، میتوان آن را به صورت یک نوع بدون پرانتز نوشت.
پارامتر ورودی نهایی در امضای یک تابع میتواند دارای نوعی باشد که با پیشوند شروع میشود.... تابعی با چنین پارامتری، متغیر (variadic) نامیده میشود و میتواند با صفر یا چند آرگومان برای آن پارامتر فراخوانی شود.
func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)یک نوع رابط، یک مجموعه نوع را تعریف میکند. یک متغیر از نوع رابط میتواند مقداری از هر نوعی را که در مجموعه نوع رابط است، ذخیره کند. گفته میشود که چنین نوعی، رابط را پیادهسازی میکند. مقدار یک متغیر از نوع رابط که مقداردهی اولیه نشده است، است nil.
InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem = MethodElem | TypeElem .
MethodElem = MethodName Signature .
MethodName = identifier .
TypeElem = TypeTerm { "|" TypeTerm } .
TypeTerm = Type | UnderlyingType .
UnderlyingType = "~" Type .یک نوع رابط توسط لیستی از عناصر رابط مشخص میشود. یک عنصر رابط یا یک متد است یا یک عنصر نوع، که در آن یک عنصر نوع، ترکیبی از یک یا چند اصطلاح نوع است. یک اصطلاح نوع یا یک نوع واحد است یا یک نوع زیربنایی واحد.
در ابتداییترین شکل خود، یک رابط، لیستی (احتمالاً خالی) از متدها را مشخص میکند. مجموعه نوع تعریف شده توسط چنین رابطی، مجموعهای از انواع است که تمام آن متدها را پیادهسازی میکند و مجموعه روش مربوطه دقیقاً شامل متدهایی است که توسط رابط مشخص شدهاند. رابطهایی که مجموعه نوع آنها را میتوان به طور کامل توسط لیستی از متدها تعریف کرد، رابطهای پایه نامیده میشوند.
// A simple File interface.
interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}نام هر متد که به صراحت مشخص شده است باید منحصر به فرد باشد و خالی نباشد.
interface {
String() string
String() string // illegal: String not unique
_(x int) // illegal: method must have non-blank name
}بیش از یک نوع میتواند یک رابط را پیادهسازی کند. برای مثال، اگر دو نوع S1 و S2 دارای مجموعه متد باشند
func (p T) Read(p []byte) (n int, err error)
func (p T) Write(p []byte) (n int, err error)
func (p T) Close() error(که در آن T`or` به معنای `or` یا S1`or` است S2) آنگاه رابط توسط هر دو `and` File پیادهسازی میشود، صرف نظر از اینکه ` and` چه متدهای دیگری ممکن است داشته باشند یا به اشتراک بگذارند.S1S2S1S2
هر نوعی که عضوی از مجموعه انواع یک رابط باشد، آن رابط را پیادهسازی میکند. هر نوع دادهای ممکن است چندین رابط مجزا را پیادهسازی کند. برای مثال، همه نوعها رابط خالی را پیادهسازی میکنند که نشاندهنده مجموعه همه انواع (غیر رابط) است:
interface{}به طور مشابه، این مشخصات رابط را در نظر بگیرید که در یک اعلان نوع برای تعریف رابطی به نام زیر ظاهر میشود Locker:
آنها Locker رابط و همچنین File رابط را پیاده سازی می کنند.
type Locker interface {
Lock()
Unlock()
}func (p T) Lock() { … }
func (p T) Unlock() { … }در یک شکل کمی کلیتر، یک رابط T ممکن است از یک نام نوع رابط (احتمالاً واجد شرایط) E به عنوان یک عنصر رابط استفاده کند. این کار در [Go 1.14] تعبیه رابط نامیده میشود. مجموعه نوع تقاطع مجموعه نوعهای تعریف شده توسط متدهای صریحاً اعلام شده 's و مجموعه نوعهای رابطهای تعبیه شده 's است. به عبارت دیگر، مجموعه نوع مجموعهای از تمام انواعی است که تمام متدهای صریحاً اعلام شده ' و همچنین تمام متدهای [Go 1.18] را پیادهسازی میکنند.ETTTTTTE
type Reader interface {
Read(p []byte) (n int, err error)
Close() error
}
type Writer interface {
Write(p []byte) (n int, err error)
Close() error
}
// ReadWriter's methods are Read, Write, and Close.
type ReadWriter interface {
Reader // includes methods of Reader in ReadWriter's method set
Writer // includes methods of Writer in ReadWriter's method set
}هنگام تعبیه رابطها، متدهایی با نامهای یکسان باید امضاهای یکسانی داشته باشند.
type ReadCloser interface {
Reader // includes methods of Reader in ReadCloser's method set
Close() // illegal: signatures of Reader.Close and Close are different
}در عمومیترین شکل خود، یک عنصر رابط میتواند یک اصطلاح نوع دلخواه T یا اصطلاحی به شکلی باشد ~T که نوع زیرین را مشخص میکند T، یا مجموعهای از اصطلاحات باشد [Go 1.18]. این عناصر همراه با مشخصات متد، تعریف دقیق مجموعه نوع یک رابط را به شرح زیر امکانپذیر میکنند:t1|t2|…|tn
مجموعه نوع رابط خالی، مجموعه تمام انواع غیر رابط است. مجموعه نوع یک رابط غیر تهی، محل تقاطع مجموعه نوع عناصر رابط آن است. مجموعه نوع مشخصات یک متد، مجموعه تمام انواع غیر رابطی است که مجموعه متدهای آنها شامل آن متد میشود. مجموعه نوع یک اصطلاح نوع غیر رابط، مجموعهای است که فقط از آن نوع تشکیل شده است. مجموعه نوع یک عبارت به این شکل ~T، مجموعه تمام نوعهایی است که نوع زیربنایی آنها است T. عبارت «مجموعه تمام انواع غیر واسط» نه تنها به تمام انواع (غیر واسط) اعلام شده در برنامه فعلی اشاره دارد، بلکه به تمام انواع ممکن در تمام برنامههای ممکن نیز اشاره دارد و از این رو نامتناهی است. به طور مشابه، با توجه به مجموعه تمام انواع غیر واسط که یک متد خاص را پیادهسازی میکنند، اشتراک مجموعه متدهای آن نوعها دقیقاً شامل آن متد خواهد بود، حتی اگر تمام انواع موجود در برنامه فعلی همیشه آن متد را با متد دیگری جفت کنند.
طبق ساختار، مجموعه نوع یک رابط هرگز شامل یک نوع رابط نیست.
به عبارت دیگر ~T، نوع زیرینِ T باید خودش باشد و T نمیتواند یک رابط باشد.
// An interface representing only the type int.
interface {
int
}
// An interface representing all types with underlying type int.
interface {
~int
}
// An interface representing all types with underlying type int that implement the String method.
interface {
~int
String() string
}
// An interface representing an empty type set: there is no type that is both an int and a string.
interface {
int
string
}عناصر اتحادیه، اتحادیههای مجموعههای نوع را نشان میدهند:
type MyInt int
interface {
~[]byte // the underlying type of []byte is itself
~MyInt // illegal: the underlying type of MyInt is not MyInt
~error // illegal: error is an interface
}نوع T در یک عبارت به شکل Tor ~T نمیتواند یک پارامتر نوع باشد، و مجموعههای نوع تمام عبارات غیر رابط باید به صورت جفتی مجزا باشند (تقاطع جفتی مجموعههای نوع باید خالی باشد). با توجه به یک پارامتر نوع P:
// The Float interface represents all floating-point types
// (including any named types whose underlying types are
// either float32 or float64).
type Float interface {
~float32 | ~float64
}محدودیت پیادهسازی: یک یونیون (با بیش از یک اصطلاح) نمیتواند شامل شناسه یا رابطهای ازپیشاعلانشدهای باشد که روشها را مشخص میکنند، یا رابطهایی را که روشها را مشخص میکنند، جاسازی کند.comparablecomparable
interface {
P // illegal: P is a type parameter
int | ~P // illegal: P is a type parameter
~int | MyInt // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
float32 | Float // overlapping type sets but Float is an interface
}رابطهایی که پایه نیستند، فقط میتوانند به عنوان محدودیتهای نوع یا به عنوان عناصر رابطهای دیگر به عنوان محدودیت استفاده شوند. آنها نمیتوانند نوع مقادیر یا متغیرها یا اجزای سایر انواع غیر رابط باشند.
یک نوع رابط T نمیتواند عنصری از نوع را که T مستقیماً یا غیرمستقیم شامل، یا جاسازی شده است، در خود جای دهد.
var x Float // illegal: Float is not a basic interface
var x interface{} = Float(nil) // illegal
type Floatish struct {
f Float // illegal
}// illegal: Bad may not embed itself
type Bad interface {
Bad
}
// illegal: Bad1 may not embed itself using Bad2
type Bad1 interface {
Bad2
}
type Bad2 interface {
Bad1
}
// illegal: Bad3 may not embed a union containing Bad3
type Bad3 interface {
~int | ~string | Bad3
}
// illegal: Bad4 may not embed an array containing Bad4 as element type
type Bad4 interface {
[10]Bad4
}یک نوع، T یک رابط را پیادهسازی میکند I اگر
T یک رابط است و مجموعه نوع T زیرمجموعهای از مجموعه نوع است I. مقداری از نوع، T اگر رابط را پیادهسازی کند، آن را پیادهسازی میکند T.
یک map، گروهی نامرتب از عناصر از یک نوع است که نوع عنصر نامیده میشود و توسط مجموعهای از کلیدهای منحصر به فرد از نوع دیگری که نوع کلید نامیده میشود، اندیسگذاری شده است. مقدار یک map بدون مقدار اولیه است nil.
MapType = "map" "[" KeyType "]" ElementType .
KeyType = Type .عملگرهای مقایسهای == و!=باید برای عملوندهایی از نوع کلید به طور کامل تعریف شوند؛ بنابراین نوع کلید نباید یک تابع، map یا slice باشد. اگر نوع کلید از نوع رابط باشد، این عملگرهای مقایسهای باید برای مقادیر کلید پویا تعریف شوند؛ عدم موفقیت باعث ایجاد اختلال در زمان اجرا خواهد شد.
map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}تعداد عناصر map، طول آن نامیده میشود. برای یک map m، میتوان آن را با استفاده از تابع داخلی کشف کرد و ممکن است در حین اجرا تغییر کند. عناصر را میتوان در حین اجرا با استفاده از انتسابها len اضافه کرد و با عبارات اندیس بازیابی کرد؛ آنها را میتوان با تابع داخلی and حذف کرد.deleteclear
یک مقدار map جدید و خالی با استفاده از تابع داخلی ساخته میشود make که نوع map و یک اشارهگر ظرفیت اختیاری را به عنوان آرگومان دریافت میکند:
make(map[string]int)
make(map[string]int, 100)ظرفیت اولیه، اندازه آن را محدود نمیکند: mapها (maps) به استثنای mapها (maps)، برای گنجاندن تعداد موارد ذخیره شده در آنها رشد میکنند nil. یک nilmap معادل یک map خالی است، با این تفاوت که هیچ عنصری نمیتواند اضافه شود.
یک کانال مکانیزمی برای اجرای همزمان توابع فراهم میکند تا با ارسال و دریافت مقادیری از یک نوع عنصر مشخص، ارتباط برقرار کنند. مقدار یک کانال مقداردهی اولیه نشده است nil.
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .یک مقدار کانال جدید و مقداردهی اولیه شده را میتوان با استفاده از تابع داخلی ایجاد کرد make، که نوع کانال و یک ظرفیت اختیاری را به عنوان آرگومان دریافت میکند:
chan T // can be used to send and receive values of type T
chan<- float64 // can only be used to send float64s
<-chan int // can only be used to receive intsساخت (چان عدد صحیح، ۱۰۰) ظرفیت، بر حسب تعداد عناصر، اندازه بافر در کانال را تعیین میکند. اگر ظرفیت صفر یا وجود نداشته باشد، کانال بافر نشده و ارتباط تنها زمانی موفقیتآمیز است که هم فرستنده و هم گیرنده آماده باشند. در غیر این صورت، اگر بافر پر نباشد (ارسال) یا خالی نباشد (دریافت)، کانال بافر شده و ارتباط بدون مسدود شدن موفقیتآمیز است. یک nil کانال هرگز برای ارتباط آماده نیست.
chan<- chan int // same as chan<- (chan int)
chan<- <-chan int // same as chan<- (<-chan int)
<-chan <-chan int // same as <-chan (<-chan int)
chan (<-chan int)یک کانال را میتوان با تابع داخلی بست close. فرم انتساب چند مقداری عملگر دریافت گزارش میدهد که آیا مقدار دریافتی قبل از بسته شدن کانال ارسال شده است یا خیر.
make(chan int, 100)یک کانال واحد میتواند در دستورات ارسال، عملیات دریافت و فراخوانی توابع داخلی cap و توسط هر تعداد گوروتین بدون همگامسازی بیشتر استفاده شود len. کانالها به عنوان صفهای اولین ورودی-اولین خروجی عمل میکنند. برای مثال، اگر یک گوروتین مقادیری را در یک کانال ارسال کند و گوروتین دوم آنها را دریافت کند، مقادیر به ترتیب ارسال دریافت میشوند.
مقادیر انواع ازپیشاعلانشده (برای رابطها any و... به زیر مراجعه کنید error)، آرایهها و ساختارها مستقل هستند: هر مقدار از این نوع شامل یک کپی کامل از تمام دادههای آن است و متغیرهایی از چنین نوعهایی کل مقدار را ذخیره میکنند. به عنوان مثال، یک متغیر آرایه، فضای ذخیرهسازی (متغیرها) را برای تمام عناصر آرایه فراهم میکند. مقادیر صفر مربوطه مختص انواع مقادیر هستند؛ آنها هرگز... نیستند nil.
مقادیر اشارهگر، تابع، slice، map و کانال غیر تهی حاوی ارجاعاتی به دادههای زیربنایی هستند که ممکن است توسط چندین مقدار به اشتراک گذاشته شوند:
مقدار اشارهگر، ارجاعی به متغیری است که مقدار نوع پایه اشارهگر را در خود نگه میدارد. مقدار یک تابع شامل ارجاعاتی به تابع (احتمالاً ناشناس) و متغیرهای محصور در آن است. مقدار یک slice شامل طول slice، ظرفیت آن و ارجاعی به آرایهی زیرین آن است. مقدار یک map یا کانال، ارجاعی به ساختار دادهی مختص پیادهسازی آن map یا کانال است. یک مقدار رابط میتواند مستقل باشد یا بسته به نوع پویای رابط، حاوی ارجاعاتی به دادههای زیربنایی باشد. شناسه ازپیشاعلانشده nil برای انواعی که مقادیرشان میتواند شامل ارجاعات باشد، مقدار صفر است.
وقتی چندین مقدار، دادههای زیربنایی مشترکی دارند، تغییر یک مقدار ممکن است مقدار دیگری را تغییر دهد. برای مثال، تغییر یک عنصر از یک slice، آن عنصر را در آرایه زیربنایی برای همه sliceهایی که آرایه را به اشتراک میگذارند، تغییر میدهد.
هر نوع یک نوع زیرین T دارد: اگر یکی از انواع بولی، عددی یا رشتهای ازپیشاعلانشده یا یک نوع لیترال باشد، نوع زیرین مربوطه، خودش است. در غیر این صورت، نوع زیرین، نوع زیرین نوعی است که در اعلان آن به آن اشاره میشود. برای یک پارامتر نوع، نوع زیرین قید نوع آن است که همیشه یک رابط است.TTTT
type (
A1 = string
A2 = A1
)
type (
B1 string
B2 B1
B3 []B1
B4 B3
)
func f[P any](x P) { … }دو نوع یا یکسان ("یکسان") هستند یا متفاوت.
یک نوع نامگذاری شده همیشه با هر نوع دیگری متفاوت است. در غیر این صورت، دو نوع در صورتی یکسان هستند که نوع لیترالهای زیربنایی آنها از نظر ساختاری معادل باشند؛ یعنی ساختار لیترال یکسانی داشته باشند و اجزای مربوطه انواع یکسانی داشته باشند. به طور مفصل:
دو نوع آرایه در صورتی یکسان هستند که نوع عناصر یکسان و طول آرایه یکسانی داشته باشند. دو نوع slice در صورتی یکسان هستند که انواع عناصر یکسانی داشته باشند. دو نوع ساختار در صورتی یکسان هستند که توالی فیلدهای یکسانی داشته باشند، و اگر جفت فیلدهای متناظر دارای نامهای یکسان، انواع یکسان و برچسبهای یکسان باشند، و هر دو تعبیه شده باشند یا هر دو تعبیه نشده باشند. نامهای فیلدهای صادر نشده از بستههای مختلف همیشه متفاوت هستند. دو نوع اشارهگر در صورتی یکسان هستند که نوع پایه یکسانی داشته باشند. دو نوع تابع در صورتی یکسان هستند که تعداد پارامترها و مقادیر نتیجه یکسانی داشته باشند، نوع پارامتر و نتیجه متناظر آنها یکسان باشد، و یا هر دو تابع متغیر باشند یا هیچکدام نباشند. لازم نیست نام پارامتر و نتیجه با هم مطابقت داشته باشند. دو نوع رابط در صورتی یکسان هستند که مجموعه نوع یکسانی را تعریف کنند. دو نوع map در صورتی یکسان هستند که انواع کلید و عنصر یکسانی داشته باشند. دو نوع کانال در صورتی یکسان هستند که نوع المانهای یکسان و جهت یکسانی داشته باشند. دو نوع نمونهسازیشده در صورتی یکسان هستند که انواع تعریفشده و تمام آرگومانهای نوع آنها یکسان باشند. با توجه به اعلامیههای
این انواع یکسان هستند:
type (
A0 = []string
A1 = A0
A2 = struct{ a, b int }
A3 = int
A4 = func(A3, float64) *A0
A5 = func(x int, _ float64) *[]string
B0 A0
B1 []string
B2 struct{ a, b int }
B3 struct{ a, c int }
B4 func(int, float64) *B0
B5 func(x int, y float64) *A1
C0 = B0
D0[P1, P2 any] struct{ x P1; y P2 }
E0 = D0[int, string]
)A3 و عدد صحیح
A0, A1, and []string
A2 and struct{ a, b int }
A3 and int
A4, func(int, float64) *[]string, and A5
B0 and C0
D0[int, string] and E0
[]int and []int
struct{ a, b *B5 } and struct{ a, b *B5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5بی۰ و سی۰
مقداری x از نوع، در صورتی قابل انتساب به متغیری از نوع (" قابل انتساب به " است) V است که یکی از شرایط زیر برقرار باشد:TxT
V و T یکسان هستند. V و انواع اساسی T یکسانی دارند اما پارامترهای نوع نیستند و حداقل یکی از یا یک نوع نامگذاری شده نیست.VT V و T از نوع کانالهایی با انواع عناصر یکسان هستند، V یک کانال دوطرفه است، و حداقل یکی از انواع نامگذاری شده V است یا T نیست. T یک نوع رابط است، اما یک پارامتر نوع نیست، و x را پیادهسازی میکند T. x شناسه از پیش تعریف شده است nil و T یک اشاره گر، تابع، slice، map، کانال یا نوع رابط است، اما یک پارامتر نوع نیست. x یک ثابت بدون نوع است که با مقداری از نوع قابل نمایش است T. علاوه بر این، اگر x نوع V یا T پارامترهای نوع باشند، در صورت وجود یکی از شرایط زیر، x میتوان آنها را به متغیری از نوع اختصاص داد:T
x شناسه از پیش اعلام شده است nil، T یک پارامتر نوع است و x به هر نوع در T مجموعه نوع 's قابل انتساب است. V یک نوع نامگذاری شده نیست، T یک پارامتر نوع است و x به هر نوع در T مجموعه نوع 's قابل انتساب است. V یک پارامتر نوع است و T یک نوع نامگذاری شده نیست، و مقادیر هر نوع در V مجموعه نوع 's قابل انتساب به هستند T.
یک ثابت در صورتی که یکی از شرایط زیر برقرار باشد، با مقداری از نوع قابل نمایش x است، که در آن پارامتر نوع نیست:TT
x در مجموعه مقادیری است که توسط تعیین میشوند T. T یک نوع اعشاری است و x میتواند بدون سرریز تا دقت s گرد شود T. گرد کردن از قوانین گرد کردن به زوج IEEE 754 استفاده میکند، اما با یک صفر منفی IEEE که بیشتر به یک صفر بدون علامت ساده شده است. توجه داشته باشید که مقادیر ثابت هرگز منجر به صفر منفی IEEE، NaN یا بینهایت نمیشوند. T یک نوع پیچیده است، و x اجزای و با مقادیر نوع مؤلفه قابل نمایش هستند (یا).real(x)imag(x)Tfloat32float64 اگر T یک پارامتر نوع باشد، x با مقداری از نوع قابل نمایش است، T اگر x با مقداری از هر نوع در T مجموعه نوع قابل نمایش باشد.
x T x با مقدار T قابل نمایش است زیرا
x T x is representable by a value of T because
'a' byte 97 is in the set of byte values
97 rune rune is an alias for int32, and 97 is in the set of 32-bit integers
"foo" string "foo" is in the set of string values
1024 int16 1024 is in the set of 16-bit integers
42.0 byte 42 is in the set of unsigned 8-bit integers
1e10 uint64 10000000000 is in the set of unsigned 64-bit integers
2.718281828459045 float32 2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
-1e-1000 float64 -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
0i int 0 is an integer value
(42 + 0i) float32 42.0 (with zero imaginary part) is in the set of float32 values
x T x is not representable by a value of T because
0 bool 0 is not in the set of boolean values
'a' string 'a' is a rune, it is not in the set of string values
1024 byte 1024 is not in the set of unsigned 8-bit integers
-1 uint16 -1 is not in the set of unsigned 16-bit integers
1.1 int 1.1 is not an integer value
42i float32 (0 + 42i) is not in the set of float32 values
1e1000 float64 1e1000 overflows to IEEE +Inf after rounding
بایت ۹۷ از نوع «a» در مجموعه مقادیر بایت قرار دارد. رشته "foo" در مجموعه مقادیر رشتهای قرار دارد. 2.718281828459045 float32 2.718281828459045 به 2.7182817 گرد میشود که در مجموعه مقادیر float32 قرار دارد. -1e-1000 float64 -1e-1000 به IEEE -0.0 که به صورت سادهتر به 0.0 تبدیل میشود، گرد میشود. x T x با مقدار T قابل نمایش نیست زیرا
رشته 'a' 'a' یک رون است، در مجموعه مقادیر رشتهای نیست. -1 uint16 -1 در مجموعه اعداد صحیح ۱۶ بیتی بدون علامت نیست.
مجموعه متدهای یک نوع، متدهایی را که میتوانند روی یک عملوند از آن نوع فراخوانی شوند، تعیین میکند. هر نوع دارای یک مجموعه متد (احتمالاً خالی) مرتبط با خود است:
مجموعه متدهای یک نوع تعریفشده T شامل تمام متدهایی است که با نوع گیرنده (receiver type) اعلان شدهاند T. مجموعه متدهای یک اشارهگر به یک نوع تعریفشده T(که در آن T نه اشارهگر است و نه رابط) مجموعهای از تمام متدهای اعلامشده با receiver *T یا است T. مجموعه متدهای یک نوع رابط، محل تقاطع مجموعه متدهای هر نوع در مجموعه نوع رابط است (مجموعه متد حاصل معمولاً فقط مجموعه متدهای اعلام شده در رابط است). قوانین بیشتری در مورد ساختارها (و اشارهگر به ساختارها) حاوی فیلدهای تعبیهشده اعمال میشود، همانطور که در بخش انواع ساختار توضیح داده شده است. هر نوع دیگری دارای یک مجموعه متد خالی است.
در یک مجموعه متد، هر متد باید یک نام متد منحصر به فرد و غیر خالی داشته باشد.
یک بلوک، یک توالی احتمالاً خالی از اعلانها و دستورات است که درون براکتهای منطبق قرار میگیرند.
Block = "{" StatementList "}" .
StatementList = { Statement ";" } .علاوه بر بلوکهای صریح در کد منبع، بلوکهای ضمنی نیز وجود دارند:
بلوک universe شامل تمام متن منبع Go میشود. هر بسته دارای یک بلوک بسته است که شامل تمام متن منبع Go برای آن بسته است. هر فایل یک بلوک فایل دارد که شامل تمام متن منبع Go در آن فایل است. هر دستور "if"، "for" و "switch" در بلوک ضمنی خود قرار دارد. هر بند در یک دستور "switch" یا "select" به عنوان یک بلوک ضمنی عمل میکند. بلوکها تودرتو هستند و بر دامنه نفوذ تأثیر میگذارند.
یک اعلان، یک شناسه غیر خالی را به یک ثابت، نوع، پارامتر نوع، متغیر، تابع، برچسب یا بسته متصل میکند. هر شناسهای در یک برنامه باید اعلان شود. هیچ شناسهای را نمیتوان دو بار در یک بلوک اعلان کرد و هیچ شناسهای را نمیتوان هم در بلوک فایل و هم در بلوک بسته اعلان کرد.
شناسه خالی میتواند مانند هر شناسه دیگری در یک اعلان استفاده شود، اما یک اتصال (binding) معرفی نمیکند و بنابراین اعلان نمیشود. در بلوک بسته، شناسه فقط میتواند برای اعلانهای تابع init استفاده شود و مانند شناسه خالی، اتصال جدیدی معرفی نمیکند.init
Declaration = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .دامنه یک شناسه اعلامشده، میزان متن منبع است که در آن شناسه به ثابت، نوع، متغیر، تابع، برچسب یا بسته مشخصشده اشاره دارد.
زبان Go از نظر لغوی با استفاده از بلوکها محدود شده است:
محدوده یک شناسه از پیش اعلام شده، بلوک universe است. دامنه یک شناسه که نشاندهنده یک ثابت، نوع، متغیر یا تابع (اما نه متد) است که در سطح بالا (خارج از هر تابعی) اعلام شده است، بلوک بسته است. دامنه نام بسته یک بسته وارداتی، بلوک فایلی از فایلی است که حاوی اعلان واردات است. محدودهی یک شناسه که نشاندهندهی یک گیرندهی متد، پارامتر تابع یا متغیر نتیجه است، بدنهی تابع است. دامنه یک شناسه که نشاندهنده پارامتر نوع یک تابع است یا توسط یک گیرنده متد اعلام میشود، پس از نام تابع شروع میشود و در انتهای بدنه تابع پایان مییابد. دامنه یک شناسه که پارامتر نوع یک نوع را نشان میدهد، پس از نام نوع شروع میشود و در انتهای TypeSpec پایان مییابد. دامنه یک شناسه ثابت یا متغیر که درون یک تابع اعلان میشود، از انتهای ConstSpec یا VarSpec (ShortVarDecl برای اعلان متغیرهای کوتاه) شروع میشود و در انتهای داخلیترین بلوک حاوی آن پایان مییابد. دامنه یک شناسه نوع که درون یک تابع تعریف شده است، از شناسه موجود در TypeSpec شروع میشود و در انتهای داخلیترین بلوک حاوی آن پایان مییابد. شناسهای که در یک بلوک اعلام شده است، میتواند در یک بلوک داخلی دوباره اعلام شود. در حالی که شناسهی اعلان داخلی در محدودهی تعریف است، نشاندهندهی موجودیتی است که توسط اعلان داخلی اعلام شده است.
بند package یک اعلان نیست؛ نام package در هیچ محدودهای ظاهر نمیشود. هدف آن شناسایی فایلهای متعلق به یک package و تعیین نام پیشفرض package برای اعلانهای import است.
برچسبها توسط دستورات برچسبگذاری شده اعلان میشوند و در دستورات "break"، "continue" و "goto" استفاده میشوند. تعریف برچسبی که هرگز استفاده نمیشود، غیرقانونی است. برخلاف سایر شناسهها، برچسبها دارای محدوده بلوکی نیستند و با شناسههایی که برچسب نیستند، تداخل ندارند. محدوده یک برچسب، بدنه تابعی است که در آن اعلان شده است و بدنه هر تابع تودرتو را شامل نمیشود.
شناسه خالی با کاراکتر زیرخط (_) نمایش داده میشود _. این شناسه به عنوان یک جاینگهدار ناشناس به جای یک شناسه معمولی (غیر خالی) عمل میکند و در اعلانها، به عنوان یک عملوند و در دستورات انتساب معنای خاصی دارد.
انواع:
Types:
any bool byte comparable
complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
Constants:
true false iota
Zero value:
nil
Functions:
append cap clear close complex copy delete imag len
make max min new panic print println real recover
مقدار صفر:
توابع:
یک شناسه میتواند برای دسترسی به آن از بستهی دیگری صادر شود. یک شناسه در صورتی صادر میشود که هر دو مورد زیر برقرار باشد:
اولین کاراکتر نام شناسه یک حرف بزرگ یونیکد است (دسته کاراکتر یونیکد Lu)؛ و شناسه در بلوک پکیج تعریف شده است یا نام فیلد یا نام متد است. سایر شناسهها صادر نمیشوند.
با توجه به مجموعهای از شناسهها، یک شناسه در صورتی منحصر به فرد نامیده میشود که با سایر شناسههای موجود در مجموعه متفاوت باشد. دو شناسه در صورتی متفاوت هستند که املای متفاوتی داشته باشند، یا در بستههای مختلف ظاهر شوند و صادر نشوند. در غیر این صورت، یکسان هستند.
یک اعلان ثابت، فهرستی از شناسهها (نام ثابتها) را به مقادیر فهرستی از عبارات ثابت متصل میکند. تعداد شناسهها باید برابر با تعداد عبارات باشد و شناسه n ام در سمت چپ به مقدار عبارت n ام در سمت راست متصل میشود.
ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .اگر نوع وجود داشته باشد، همه ثابتها نوع مشخص شده را میگیرند و عبارات باید به آن نوع قابل انتساب باشند، که نباید یک پارامتر نوع باشد. اگر نوع حذف شود، ثابتها انواع منحصر به فرد عبارات مربوطه را میگیرند. اگر مقادیر عبارت، ثابتهای بدون نوع باشند، ثابتهای اعلام شده بدون نوع باقی میمانند و شناسههای ثابت، مقادیر ثابت را نشان میدهند. به عنوان مثال، اگر عبارت یک لیترال ممیز شناور باشد، شناسه ثابت، یک ثابت ممیز شناور را نشان میدهد، حتی اگر بخش کسری لیترال صفر باشد.
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // untyped floating-point constant
const (
size int64 = 1024
eof = -1 // untyped integer constant
)
const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", untyped integer and string constants
const u, v float32 = 0, 3 // u = 0.0, v = 3.0در یک const لیست اعلان پرانتزدار، لیست عبارات میتواند از هر کدام به جز اولین ConstSpec حذف شود. چنین لیست خالی معادل جایگزینی متنی اولین لیست عبارات غیر خالی قبلی و نوع آن در صورت وجود است. بنابراین حذف لیست عبارات معادل تکرار لیست قبلی است. تعداد شناسهها باید برابر با تعداد عبارات موجود در لیست قبلی باشد. این مکانیزم همراه با iota مولد ثابت، اعلان سبک وزن مقادیر متوالی را امکانپذیر میکند:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Partyday
numberOfDays // this constant is not exported
)در یک اعلان ثابت، شناسه از پیش اعلان شده، ثابتهای iota صحیح بدون نوع متوالی را نشان میدهد. مقدار آن، اندیس ConstSpec مربوطه در اعلان آن ثابت است که از صفر شروع میشود. میتوان از آن برای ساخت مجموعهای از ثابتهای مرتبط استفاده کرد:
const (
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota == 0)
b = 1 << iota // b == 2 (iota == 1)
c = 3 // c == 3 (iota == 2, unused)
d = 1 << iota // d == 8 (iota == 3)
)
const (
u = iota * 42 // u == 0 (untyped integer constant)
v float64 = iota * 42 // v == 42.0 (float64 constant)
w = iota * 42 // w == 84 (untyped integer constant)
)
const x = iota // x == 0
const y = iota // y == 0طبق تعریف، چندین استفاده از iotaConstSpec در یک ConstSpec، همگی مقدار یکسانی دارند:
const (
bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0 (iota == 0)
bit1, mask1 // bit1 == 2, mask1 == 1 (iota == 1)
_, _ // (iota == 2, unused)
bit3, mask3 // bit3 == 8, mask3 == 7 (iota == 3)
)این مثال آخر از تکرار ضمنی آخرین لیست عبارات غیر تهی سوءاستفاده میکند.
یک اعلان نوع، یک شناسه، به نام نوع، را به یک نوع متصل میکند. اعلانهای نوع به دو شکل ارائه میشوند: اعلانهای نام مستعار و تعاریف نوع.
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .AliasDecl = identifier [ TypeParameters ] "=" Type .اگر اعلان نام مستعار پارامترهای نوع [Go 1.24] را مشخص کند، نام نوع نشاندهنده یک نام مستعار عمومی است. نامهای مستعار عمومی باید هنگام استفاده، نمونهسازی شوند.
type (
nodeList = []*Node // nodeList and []*Node are identical types
Polar = polar // Polar and polar denote identical types
)در یک اعلان نام مستعار، نوع داده شده نمیتواند پارامتر نوعی باشد که در همان اعلان اعلان شده است.
type set[P comparable] = map[P]booltype A[P any] = P // illegal: P is a type parameter declared in the declaration of A
func f[P any]() {
type A = P // ok: T is a type parameter declared by the enclosing function
}تعریف نوع، یک نوع جدید و متمایز با همان نوع و عملیات زیربنایی نوع داده شده ایجاد میکند و یک شناسه، به نام نام نوع، را به آن متصل میکند.
TypeDef = identifier [ TypeParameters ] Type .نوع جدید، نوع تعریفشده نامیده میشود. این نوع با هر نوع دیگری، از جمله نوعی که از آن ایجاد شده است، متفاوت است.
type (
Point struct{ x, y float64 } // Point and struct{ x, y float64 } are different types
polar Point // polar and Point denote different types
)
type TreeNode struct {
left, right *TreeNode
value any
}
type Block interface {
BlockSize() int
Encrypt(src, dst []byte)
Decrypt(src, dst []byte)
}یک نوع تعریفشده ممکن است متدهایی مرتبط با خود داشته باشد. این نوع هیچ متدی را که به نوع دادهشده وابسته باشد، به ارث نمیبرد، اما مجموعه متدهای یک نوع رابط یا عناصر یک نوع مرکب بدون تغییر باقی میمانند:
// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct { /* Mutex fields */ }
func (m *Mutex) Lock() { /* Lock implementation */ }
func (m *Mutex) Unlock() { /* Unlock implementation */ }
// NewMutex has the same composition as Mutex but its method set is empty.
type NewMutex Mutex
// The method set of PtrMutex's underlying type *Mutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex
// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its embedded field Mutex.
type PrintableMutex struct {
Mutex
}
// MyBlock is an interface type that has the same method set as Block.
type MyBlock Blockتعاریف نوع ممکن است برای تعریف انواع مختلف بولی، عددی یا رشتهای و مرتبط کردن متدها با آنها استفاده شود:
type TimeZone int
const (
EST TimeZone = -(5 + iota)
CST
MST
PST
)
func (tz TimeZone) String() string {
return fmt.Sprintf("GMT%+dh", tz)
}اگر تعریف نوع، پارامترهای نوع را مشخص کند، نام نوع نشاندهنده یک نوع عمومی است. انواع عمومی باید هنگام استفاده، نمونهسازی شوند.
type List[T any] struct {
next *List[T]
value T
}در تعریف نوع، نوع داده شده نمیتواند یک پارامتر نوع باشد.
type T[P any] P // illegal: P is a type parameter
func f[P any]() {
type L P // illegal: P is a type parameter declared by the enclosing function
}یک نوع عمومی همچنین ممکن است متدهایی مرتبط با خود داشته باشد. در این حالت، گیرندههای متد باید به همان تعداد پارامترهای نوع که در تعریف نوع عمومی وجود دارد، تعریف شوند.
// The method Len returns the number of elements in the linked list l.
func (l *List[T]) Len() int { … }یک لیست پارامتر نوع، پارامترهای نوع یک تابع عمومی یا اعلان نوع را اعلام میکند. لیست پارامتر نوع مانند لیست پارامترهای تابع معمولی به نظر میرسد، با این تفاوت که نام پارامترهای نوع باید همگی وجود داشته باشند و لیست به جای پرانتز، درون براکت قرار میگیرد [Go 1.18].
TypeParameters = "[" TypeParamList [ "," ] "]" .
TypeParamList = TypeParamDecl { "," TypeParamDecl } .
TypeParamDecl = IdentifierList TypeConstraint .تمام نامهای غیر خالی در لیست باید منحصر به فرد باشند. هر نام یک پارامتر نوع را اعلام میکند، که یک نوع نامگذاری شده جدید و متفاوت است که به عنوان یک نگهدارنده برای یک نوع (هنوز) ناشناخته در اعلان عمل میکند. پارامتر نوع با یک آرگومان نوع پس از نمونهسازی تابع یا نوع عمومی جایگزین میشود.
[P any]
[S interface{ ~[]byte|string }]
[S ~[]E, E any]
[P Constraint[int]]
[_ any]همانطور که هر پارامتر تابع معمولی یک نوع پارامتر دارد، هر پارامتر نوع نیز یک (فرا) نوع متناظر دارد که محدودیت نوع آن نامیده میشود.
ابهام در تجزیه زمانی ایجاد میشود که لیست پارامترهای نوع برای یک نوع عمومی، یک پارامتر نوع واحد P با محدودیتی را اعلام کند C به طوری که متن P C یک عبارت معتبر را تشکیل دهد:
type T[P *C] …
type T[P (C)] …
type T[P *C|Q] …
…در این موارد نادر، لیست پارامترهای نوع از یک عبارت قابل تشخیص نیست و اعلان نوع به عنوان اعلان نوع آرایه تجزیه میشود. برای رفع ابهام، محدودیت را در یک رابط جاسازی کنید یا از یک ویرگول انتهایی استفاده کنید:
type T[P interface{*C}] …
type T[P *C,] …پارامترهای نوع همچنین میتوانند توسط مشخصات گیرندهی یک متدِ مرتبط با یک نوع عمومی اعلان شوند.
یک قید نوع، رابطی است که مجموعه آرگومانهای نوع مجاز را برای پارامتر نوع مربوطه تعریف میکند و عملیات پشتیبانی شده توسط مقادیر آن پارامتر نوع را کنترل میکند [Go 1.18].
TypeConstraint = TypeElem .اگر محدودیت یک رابط تحتاللفظی به شکل باشد interface{E}که در آن یک عنصر نوع E تعبیهشده (نه یک متد) است، در یک لیست پارامتر نوع، میتوان برای راحتی، را حذف کرد:interface{…}
[T []P] // = [T interface{[]P}]
[T ~int] // = [T interface{~int}]
[T int|string] // = [T interface{int|string}]
type Constraint ~int // illegal: ~int is not in a type parameter listاگرچه رابطهایی که پارامترهای نوع نیستند قابل مقایسه هستند، اما کاملاً قابل مقایسه نیستند و بنابراین پیادهسازی نمیشوند comparable. با این حال، آنها را برآورده میکنند comparable.
رابط comparable و رابطهایی که (مستقیم یا غیرمستقیم) جاسازی میشوند، comparable فقط میتوانند به عنوان محدودیتهای نوع استفاده شوند. آنها نمیتوانند انواع مقادیر یا متغیرها یا اجزای سایر انواع غیر رابط باشند.
int // implements comparable (int is strictly comparable)
[]byte // does not implement comparable (slices cannot be compared)
interface{} // does not implement comparable (see above)
interface{ ~int | ~string } // type parameter only: implements comparable (int, string types are strictly comparable)
interface{ comparable } // type parameter only: implements comparable (comparable implements itself)
interface{ ~int | ~[]byte } // type parameter only: does not implement comparable (slices are not comparable)
interface{ ~struct{ any } } // type parameter only: does not implement comparable (field any is not strictly comparable)یک آرگومان نوع، یک محدودیت نوع را T برآورده میکند C اگر T عنصری از مجموعه نوع تعریف شده توسط باشد C؛ به عبارت دیگر، اگر T پیادهسازی کند C. به عنوان یک استثنا، یک محدودیت نوع کاملاً قابل مقایسه ممکن است توسط یک آرگومان نوع قابل مقایسه (نه لزوماً کاملاً قابل مقایسه) نیز برآورده شود [Go 1.20]. به طور دقیقتر:
یک نوع T یک محدودیت را برآورده میکند C اگر
T وسایل C؛ یا C را میتوان به شکل نوشت interface{comparable; E}، که در آن E یک رابط پایه است و قابل مقایسه و پیادهسازی T است.E
type argument type constraint // constraint satisfaction
int interface{ ~int } // satisfied: int implements interface{ ~int }
string comparable // satisfied: string implements comparable (string is strictly comparable)
[]byte comparable // not satisfied: slices are not comparable
any interface{ comparable; int } // not satisfied: any does not implement interface{ int }
any comparable // satisfied: any is comparable and implements the basic interface any
struct{f any} comparable // satisfied: struct{f any} is comparable and implements the basic interface any
any interface{ comparable; m() } // not satisfied: any does not implement the basic interface interface{ m() }
interface{ m() } interface{ comparable; m() } // satisfied: interface{ m() } is comparable and implements the basic interface interface{ m() }به دلیل استثنا در قانون ارضای محدودیت، مقایسه عملوندهایی از نوع پارامتر ممکن است در زمان اجرا با مشکل مواجه شود (حتی اگر پارامترهای نوع قابل مقایسه همیشه کاملاً قابل مقایسه باشند).
یک اعلان متغیر، یک یا چند متغیر ایجاد میکند، شناسههای مربوطه را به آنها متصل میکند و به هر کدام یک نوع و یک مقدار اولیه میدهد.
VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (
i int
u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name] // map lookup; only interested in "found"اگر لیستی از عبارات داده شده باشد، متغیرها با عباراتی که از قوانین مربوط به دستورات انتساب پیروی میکنند، مقداردهی اولیه میشوند. در غیر این صورت، هر متغیر با مقدار صفر خود مقداردهی اولیه میشود.
اگر نوعی وجود داشته باشد، به هر متغیر آن نوع داده میشود. در غیر این صورت، به هر متغیر نوع مقدار اولیه مربوطه در انتساب داده میشود. اگر آن مقدار یک ثابت بدون نوع باشد، ابتدا به طور ضمنی به نوع پیشفرض خود تبدیل میشود؛ اگر یک مقدار بولی بدون نوع باشد، ابتدا به طور ضمنی به نوع تبدیل میشود. شناسه از پیش تعریف شده را نمیتوان برای مقداردهی اولیه متغیری بدون نوع صریح استفاده کرد.boolnil
var d = math.Sin(0.5) // d is float64
var i = 42 // i is int
var t, ok = x.(T) // t is T, ok is bool
var n = nil // illegalمحدودیت پیادهسازی: کامپایلر ممکن است تعریف یک متغیر درون بدنه تابع را در صورتی که هرگز استفاده نشود، غیرقانونی اعلام کند.
یک اعلان متغیر کوتاه از این ساختار استفاده میکند:
ShortVarDecl = IdentifierList ":=" ExpressionList .این خلاصهای از یک تعریف متغیر معمولی با عبارات مقداردهی اولیه است اما هیچ نوع دادهای ندارد:
"var" IdentifierList "=" ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe() // os.Pipe() returns a connected pair of Files and an error, if any
_, y, _ := coord(p) // coord() returns three values; only interested in y coordinateاعلانهای کوتاه متغیر ممکن است فقط درون توابع ظاهر شوند. در برخی زمینهها مانند مقداردهی اولیه برای دستورات "if"، "for" یا "switch"، میتوان از آنها برای اعلان متغیرهای موقت محلی استفاده کرد.
field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset) // redeclares offset
x, y, x := 1, 2, 3 // illegal: x repeated on left side of :=
FunctionDecl = "func" FunctionName [ TypeParameters ] Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .اگر امضای تابع، پارامترهای نتیجه را اعلام کند، لیست دستورات بدنه تابع باید با یک دستور خاتمه دهنده خاتمه یابد.
func IndexRune(s string, r rune) int {
for i, c := range s {
if c == r {
return i
}
}
// invalid: missing return statement
}اگر در اعلان تابع، پارامترهای نوع مشخص شوند، نام تابع نشاندهنده یک تابع عمومی است. یک تابع عمومی قبل از اینکه بتواند فراخوانی شود یا به عنوان مقدار استفاده شود، باید نمونهسازی شود.
func min[T ~int|~float64](x, y T) T {
if x < y {
return x
}
return y
}یک اعلان تابع بدون پارامترهای نوع ممکن است بدنه را حذف کند. چنین اعلانی، امضای تابعی را که خارج از Go پیادهسازی شده است، مانند یک روال اسمبلی، فراهم میکند.
func flushICache(begin, end uintptr) // implemented externallyیک متد، تابعی با یک گیرنده است. اعلان متد، یک شناسه، یعنی نام متد، را به یک متد متصل میکند و متد را با نوع پایه گیرنده مرتبط میسازد.
MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver = Parameters .گیرنده از طریق یک بخش پارامتر اضافی قبل از نام متد مشخص میشود. آن بخش پارامتر باید یک پارامتر غیر متغیر واحد، گیرنده، را تعریف کند. نوع آن باید یک نوع تعریف شده T یا یک اشارهگر به یک نوع تعریف شده باشد T، و احتمالاً به دنبال آن لیستی از نام پارامترهای نوع که [P1, P2, …] در داخل کروشه قرار گرفتهاند، قرار میگیرد. نوع پایه T گیرنده نامیده میشود. یک نوع پایه گیرنده نمیتواند یک اشارهگر یا نوع رابط باشد و باید در همان بستهای که متد تعریف شده است، تعریف شود. گفته میشود که متد به نوع پایه گیرنده خود متصل است و نام متد فقط در انتخابگرهای نوع یا قابل مشاهده است.T*T
یک شناسه گیرنده غیر خالی باید در امضای متد منحصر به فرد باشد. اگر مقدار گیرنده در داخل بدنه متد ارجاع داده نشود، شناسه آن ممکن است در اعلان حذف شود. همین امر به طور کلی در مورد پارامترهای توابع و متدها نیز صدق میکند.
برای یک نوع پایه، نامهای غیرخالی متدهای متصل به آن باید منحصر به فرد باشند. اگر نوع پایه از نوع struct باشد، نامهای غیرخالی متد و فیلد باید متمایز باشند.
با توجه به نوع تعریف شده Point، اعلانها
func (p *Point) Length() float64 {
return math.Sqrt(p.x * p.x + p.y * p.y)
}
func (p *Point) Scale(factor float64) {
p.x *= factor
p.y *= factor
}متدها Length و را Scale با نوع گیرنده *Point، به نوع پایه متصل کنید Point.
اگر نوع پایه گیرنده یک نوع عمومی باشد، مشخصات گیرنده باید پارامترهای نوع مربوطه را برای استفاده از روش اعلام کند. این امر پارامترهای نوع گیرنده را در دسترس روش قرار میدهد. از نظر نحوی، این اعلان پارامتر نوع مانند یک نمونه از نوع پایه گیرنده به نظر میرسد: آرگومانهای نوع باید شناسههایی باشند که پارامترهای نوع اعلام شده را نشان میدهند، یکی برای هر پارامتر نوع از نوع پایه گیرنده. نام پارامترهای نوع نیازی به مطابقت با نام پارامترهای مربوطه در تعریف نوع پایه گیرنده ندارند و همه نامهای پارامترهای غیر خالی باید در بخش پارامتر گیرنده و امضای روش منحصر به فرد باشند. محدودیتهای پارامتر نوع گیرنده توسط تعریف نوع پایه گیرنده ضمنی میشوند: پارامترهای نوع مربوطه دارای محدودیتهای مربوطه هستند.
type Pair[A, B any] struct {
a A
b B
}
func (p Pair[A, B]) Swap() Pair[B, A] { … } // receiver declares A, B
func (p Pair[First, _]) First() First { … } // receiver declares First, corresponds to A in Pairاگر نوع گیرنده با (اشارهگری به) یک نام مستعار مشخص شده باشد، آن نام مستعار نباید عمومی باشد و نباید به یک نوع عمومی نمونهسازی شده اشاره کند، نه به طور مستقیم و نه غیرمستقیم از طریق نام مستعار دیگر، و صرف نظر از جهتگیریهای اشارهگر.
type GPoint[P any] = Point
type HPoint = *GPoint[int]
type IPair = Pair[int, int]
func (*GPoint[P]) Draw(P) { … } // illegal: alias must not be generic
func (HPoint) Draw(P) { … } // illegal: alias must not denote instantiated type GPoint[int]
func (*IPair) Second() int { … } // illegal: alias must not denote instantiated type Pair[int, int]یک عبارت، محاسبه یک مقدار را با اعمال عملگرها و توابع روی عملوندها مشخص میکند.
عملوندها مقادیر اولیه در یک عبارت را نشان میدهند. یک عملوند میتواند یک مقدار تحتاللفظی، یک شناسه (احتمالاً واجد شرایط) غیر خالی باشد که نشان دهنده یک ثابت، متغیر یا تابع است، یا یک عبارت پرانتزدار باشد.
Operand = Literal | OperandName [ TypeArgs ] | "(" Expression ")" .
Literal = BasicLit | CompositeLit | FunctionLit .
BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent .نام عملوندی که نشاندهنده یک تابع عمومی است، میتواند با فهرستی از آرگومانهای نوع دنبال شود؛ عملوند حاصل یک تابع نمونهسازی شده است.
شناسه خالی میتواند فقط به عنوان یک عملوند در سمت چپ یک دستور انتساب ظاهر شود.
محدودیت پیادهسازی: اگر نوع یک عملوند، پارامتری با مجموعه نوع خالی باشد، کامپایلر نیازی به گزارش خطا ندارد. توابعی با چنین پارامترهایی را نمیتوان نمونهسازی کرد؛ هرگونه تلاشی منجر به خطا در محل نمونهسازی خواهد شد.
یک شناسه واجد شرایط، شناسهای است که با پیشوند نام بسته تعریف شده است. هم نام بسته و هم شناسه نباید خالی باشند.
QualifiedIdent = PackageName "." identifier .یک شناسه واجد شرایط به شناسهای در یک بستهی دیگر دسترسی پیدا میکند که باید وارد شود. شناسه باید صادر شود و در بلوک بستهی آن بسته اعلام شود.
math.Sin // denotes the Sin function in package mathلیترالهای مرکب هر بار که ارزیابی میشوند، مقادیر جدیدی برای ساختارها، آرایهها، sliceها و mapها ایجاد میکنند. آنها شامل نوع لیترال و به دنبال آن لیستی (احتمالاً خالی) از عناصر متصل به براکت هستند. هر عنصر میتواند به صورت اختیاری با یک کلید مربوطه همراه باشد.
CompositeLit = LiteralType LiteralValue .
LiteralType = StructType | ArrayType | "[" "..." "]" ElementType |
SliceType | MapType | TypeName [ TypeArgs ] .
LiteralValue = "{" [ ElementList [ "," ] ] "}" .
ElementList = KeyedElement { "," KeyedElement } .
KeyedElement = [ Key ":" ] Element .
Key = FieldName | Expression | LiteralValue .
FieldName = identifier .
Element = Expression | LiteralValue .کلیدی مگر اینکه LiteralType یک پارامتر نوع باشد، نوع زیرین آن باید یک نوع struct، array، slice یا map باشد (نحو این محدودیت را اعمال میکند، مگر اینکه نوع به عنوان TypeName ارائه شود). اگر LiteralType یک پارامتر نوع باشد، همه انواع موجود در مجموعه نوع آن باید نوع زیرین یکسانی داشته باشند که باید یک نوع لیترال مرکب معتبر باشد.
نوع عناصر و کلیدها باید به انواع فیلد، عنصر و کلید مربوطه در LiteralType قابل انتساب باشند؛ هیچ تبدیل اضافی وجود ندارد. کلید برای لیترالهای struct به عنوان نام فیلد، برای لیترالهای array و slice به عنوان اندیس و برای لیترالهای map به عنوان کلید تفسیر میشود. مشخص کردن چندین عنصر با نام فیلد یا مقدار کلید ثابت یکسان، خطا محسوب میشود. یک لیترال ممکن است لیست عناصر را حذف کند؛ چنین لیترالی برای نوع خود به مقدار صفر ارزیابی میشود.
ابهام در تجزیه زمانی ایجاد میشود که یک لیترال مرکب با استفاده از فرم TypeName از LiteralType به عنوان یک عملوند بین کلمه کلیدی و براکت آغازین بلوک یک دستور "if"، "for" یا "switch" ظاهر شود و لیترال مرکب در پرانتز، براکت مربع یا آکولاد محصور نشده باشد. در این مورد نادر، براکت آغازین لیترال به اشتباه به عنوان براکت معرفی کننده بلوک دستورات تجزیه میشود. برای رفع ابهام، لیترال مرکب باید درون پرانتز ظاهر شود.
if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }
برای لیترالهای ساختار بدون کلید، لیست عناصر باید شامل یک عنصر برای هر فیلد ساختار به ترتیبی باشد که فیلدها تعریف شدهاند.
هر عنصر باید یک کلید داشته باشد. هر کلید باید نام فیلدی باشد که در نوع ساختار (struct) تعریف شده است. لیست عناصر نیازی به داشتن یک عنصر برای هر فیلد ساختار ندارد. فیلدهای حذف شده مقدار صفر را برای آن فیلد دریافت میکنند. تعیین عنصر برای فیلد export نشدهی یک struct متعلق به یک پکیج متفاوت، خطا محسوب میشود. با توجه به اعلامیههای
ممکن است کسی بنویسد
type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }origin := Point3D{} // zero value for Point3D
line := Line{origin, Point3D{y: -4, z: 12.3}} // zero value for line.q.x
هر عنصر یک اندیس عدد صحیح مرتبط دارد که موقعیت آن را در آرایه مشخص میکند. عنصری که دارای کلید است، از آن کلید به عنوان اندیس خود استفاده میکند. کلید باید یک ثابت غیر منفی باشد که با مقداری از نوع ; قابل نمایش باشد int و اگر از نوع عدد صحیح باشد، باید از نوع عدد صحیح باشد. عنصری که کلید ندارد، از اندیس عنصر قبلی به اضافه یک استفاده میکند. اگر عنصر اول کلید نداشته باشد، اندیس آن صفر است. گرفتن آدرس یک لیترال مرکب، یک اشارهگر به یک متغیر منحصر به فرد که با مقدار لیترال مقداردهی اولیه شده است، ایجاد میکند.
توجه داشته باشید که مقدار صفر برای یک نوع slice یا map با مقدار اولیه اما خالی از همان نوع یکسان نیست. در نتیجه، گرفتن آدرس یک slice یا map composite literal خالی، همان تأثیر اختصاص دادن یک slice یا map جدید با new را ندارد.
طول یک آرایهی تحتاللفظی، طولی است که در نوع تحتاللفظی مشخص شده است. اگر عناصر کمتری نسبت به طول در آن ارائه شود، عناصر از دست رفته برای نوع عنصر آرایه روی مقدار صفر تنظیم میشوند. ارائه عناصری با مقادیر شاخص خارج از محدودهی شاخص آرایه، خطا محسوب میشود. این نمادگذاری،...طول آرایه را برابر با حداکثر شاخص عنصر به علاوهی یک مشخص میکند.
var pointer *Point3D = &Point3D{y: 1000}یک لیترال slice ی، کل لیترال آرایه زیرین را توصیف میکند. بنابراین طول و ظرفیت یک لیترال slice ی، حداکثر اندیس عنصر به علاوه یک است. یک لیترال slice ی به شکل زیر است:
p1 := &[]int{} // p1 points to an initialized, empty slice with value []int{} and length 0
p2 := new([]int) // p2 points to an uninitialized slice with value nil and length 0
و مخفف عملیات slice است که روی یک آرایه اعمال میشود:
buffer := [10]string{} // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5} // len(intSet) == 6
days := [...]string{"Sat", "Sun"} // len(days) == 2
[]T{x1, x2, … xn}
tmp := [n]T{x1, x2, … xn}
tmp[0 : n]
برای لیترالهای map، هر عنصر باید یک کلید داشته باشد. برای کلیدهای map غیرثابت، به بخش ترتیب ارزیابی مراجعه کنید.
در یک لیترال مرکب از نوع آرایه، slice یا map T، عناصر یا کلیدهای map که خودشان لیترال مرکب هستند، ممکن است نوع لیترال مربوطه را حذف کنند اگر با عنصر یا نوع کلید یکسان باشد T. به طور مشابه، عناصر یا کلیدهایی که آدرس لیترالهای مرکب هستند، ممکن است را حذف کنند &T وقتی که نوع عنصر یا کلید باشد *T.
[...]Point{{1.5, -3.5}, {0, 0}} // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
[][]int{{1, 2, 3}, {4, 5}} // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
[][]Point{{{0, 1}, {1, 2}}} // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
map[string]Point{"orig": {0, 0}} // same as map[string]Point{"orig": Point{0, 0}}
map[Point]string{{0, 0}: "orig"} // same as map[Point]string{Point{0, 0}: "orig"}
type PPoint *Point
[2]*Point{{1.5, -3.5}, {}} // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
[2]PPoint{{1.5, -3.5}, {}} // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}نمونههایی از آرایههای معتبر، sliceها و mapها:
// list of prime numbers
primes := []int{2, 3, 5, 7, 9, 2147483647}
// vowels[ch] is true if ch is a vowel
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
noteFrequency := map[string]float32{
"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
"G0": 24.50, "A0": 27.50, "B0": 30.87,
}یک تابع لیترال نشان دهنده یک تابع ناشناس است. تابع لیترال نمیتواند پارامترهای نوع را تعریف کند.
FunctionLit = "func" Signature FunctionBody .func(a, b int, z float64) bool { return a*b < int(z) }یک تابع تحتاللفظی میتواند به یک متغیر اختصاص داده شود یا مستقیماً فراخوانی شود.
f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)تابعهای تحتاللفظی، بستار هستند: آنها ممکن است به متغیرهایی اشاره کنند که در یک تابع اطراف تعریف شدهاند. سپس این متغیرها بین تابع اطراف و تابع تحتاللفظی به اشتراک گذاشته میشوند و تا زمانی که قابل دسترسی باشند، باقی میمانند.
عبارات اصلی، عملوندهایی برای عبارات یگانه و دودویی هستند.
PrimaryExpr = Operand |
Conversion |
MethodExpr |
PrimaryExpr Selector |
PrimaryExpr Index |
PrimaryExpr Slice |
PrimaryExpr TypeAssertion |
PrimaryExpr Arguments .
Selector = "." identifier .
Index = "[" Expression [ "," ] "]" .
Slice = "[" [ Expression ] ":" [ Expression ] "]" |
"[" [ Expression ] ":" Expression ":" Expression "]" .
TypeAssertion = "." "(" Type ")" .
Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()
ایکس f(3.1415، درست) رنگ شیء
ایکس اف نشان دهنده فیلد یا متد f مقدار x(یا گاهی اوقات *x; به پایین مراجعه کنید) است. شناسه، انتخابگر f (فیلد یا متد) نامیده میشود؛ نباید شناسه خالی باشد. نوع عبارت انتخابگر، نوع است. اگر نام یک بسته است، به بخش مربوط به شناسههای واجد شرایط مراجعه کنید.fx
x.f
یک انتخابگر f میتواند به یک فیلد یا متد f از یک نوع اشاره کند T، یا ممکن است به یک فیلد یا متد f از یک فیلد تعبیهشدهی تودرتو از اشاره داشته باشد T. تعداد فیلدهای تعبیهشدهای که برای رسیدن به آنها پیمایش میشوند، عمق آن در f نامیده میشود. عمق یک فیلد یا متد اعلامشده در برابر با صفر است. عمق یک فیلد یا متد اعلامشده در یک فیلد تعبیهشده در برابر با عمق در به علاوهی یک است.TfTfATfA
قوانین زیر در مورد انتخابگرها اعمال میشود:
برای مقداری x از نوع T یا *T که در آن T اشارهگر یا نوع رابط نیست، x.f فیلد یا متدی را در کمعمقترین عمق در T جایی که چنین چیزی وجود دارد، نشان میدهد. اگر دقیقاً یکی f با کمعمقترین عمق وجود نداشته باشد، عبارت انتخابگر غیرمجاز است.f برای مقداری x از نوع I که در آن I یک نوع رابط است، x.f به متد واقعی با نام f مقدار پویای اشاره دارد. اگر متدی با نام در مجموعه متدهای x وجود نداشته باشد، عبارت انتخابگر غیرمجاز است.fI به عنوان یک استثنا، اگر نوع "" x یک نوع اشارهگر تعریف شده باشد و (*x).f یک عبارت انتخابگر معتبر باشد که یک فیلد را نشان میدهد (اما یک متد نیست)، x.f مخفف "" است (*x).f. در تمام موارد دیگر، x.f غیرقانونی است. اگر x از نوع اشارهگر باشد و مقدار داشته باشد nil و x.f نشاندهنده یک فیلد ساختار باشد، انتساب یا ارزیابی x.f باعث ایجاد یک خطای زمان اجرا میشود. اگر x از نوع رابط باشد و مقدار را داشته باشد nil، فراخوانی یا ارزیابی متد x.f باعث ایجاد یک خطای زمان اجرا میشود.
ممکن است کسی بنویسد:
اما موارد زیر نامعتبر است:
type T0 struct {
x int
}
func (*T0) M0()
type T1 struct {
y int
}
func (T1) M1()
type T2 struct {
z int
T1
*T0
}
func (*T2) M2()
type Q *T2
var t T2 // with t.T0 != nil
var p *T2 // with p != nil and (*p).T0 != nil
var q Q = pt.z // t.z
t.y // t.T1.y
t.x // (*t.T0).x
p.z // (*p).z
p.y // (*p).T1.y
p.x // (*(*p).T0).x
q.x // (*(*q).T0).x (*q).x is a valid field selector
p.M0() // ((*p).T0).M0() M0 expects *T0 receiver
p.M1() // ((*p).T1).M1() M1 expects T1 receiver
p.M2() // p.M2() M2 expects *T2 receiver
t.M2() // (&t).M2() M2 expects *T2 receiver, see section on Calls
q.M0() // (*q).M0 is valid but not a field selector
اگر M در مجموعه متد از نوع باشد T، T.M تابعی است که به عنوان یک تابع معمولی با همان آرگومانهایی که M با یک آرگومان اضافی که گیرنده متد است، پیشوند گرفته شده است، قابل فراخوانی است.
MethodExpr = ReceiverType "." MethodName .
ReceiverType = Type .یک نوع struct T با دو متد را در نظر بگیرید، Mv که گیرنده آن از نوع T و Mp است *T.
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
var t Tتی. ام. وی تابعی معادل با Mv تابع با یک گیرنده صریح به عنوان اولین آرگومان ارائه میدهد؛ دارای امضا است.
T.Mv
آن تابع را میتوان به طور معمول با یک گیرنده صریح فراخوانی کرد، بنابراین این پنج فراخوانی معادل هستند:
func(tv T, a int) intتی.اموی(7) تی.اموی(تی، ۷) به طور مشابه، عبارت
t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)
مقدار تابعی را که Mp با امضا نمایش داده میشود، برمیگرداند.
(*T).Mp
مقدار تابعی را که Mv با امضا نمایش داده میشود، برمیگرداند.
func(tp *T, f float32) float32چنین تابعی به طور غیرمستقیم از طریق گیرنده، مقداری ایجاد میکند تا به عنوان گیرنده به متد اصلی ارسال شود؛ متد، مقداری را که آدرس آن در فراخوانی تابع ارسال شده است، بازنویسی نمیکند.
(*T).Mv
مورد آخر، یک تابع دریافتکنندهی مقدار برای یک متد اشارهگر-دریافتکننده، غیرمجاز است زیرا متدهای اشارهگر-دریافتکننده در مجموعه متدهای نوع مقداری قرار ندارند.
func(tv *T, a int) intاستخراج مقدار یک تابع از متدی از نوع رابط، قانونی است. تابع حاصل، یک گیرنده صریح از آن نوع رابط میگیرد.
اگر عبارت x از نوع استاتیک باشد T و M در مجموعه متد از نوع باشد T، مقدار متد x.M نامیده میشود. مقدار متد، مقدار تابعی است که با همان آرگومانهای فراخوانی متد قابل فراخوانی است. عبارت در طول ارزیابی مقدار متد ارزیابی و ذخیره میشود. سپس کپی ذخیره شده به عنوان گیرنده در هر فراخوانی استفاده میشود که ممکن است بعداً اجرا شود.x.Mx.Mx
type S struct { *T }
type T int
func (t T) M() { print(t) }
t := new(T)
s := S{T: t}
f := t.M // receiver *t is evaluated and stored in f
g := s.M // receiver *(s.T) is evaluated and stored in g
*t = 42 // does not affect stored receivers in f and gاین نوع T میتواند از نوع رابط یا غیر رابط باشد.
همانطور که در بحث عبارات متد در بالا ذکر شد، یک نوع struct T با دو متد را در نظر بگیرید، Mv که گیرنده آن از نوع T و Mp گیرنده آن از نوع است *T.
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
var t T
var pt *T
func makeT() Tتی.ام.وی مقدار تابعی از نوع
t.Mv
این دو فراخوانی معادل هستند:
func(int) intتی.اموی(7) به طور مشابه، عبارت
t.Mv(7)
f := t.Mv; f(7)
مقدار تابعی از نوع
pt.Mp
همانند انتخابگرها، ارجاع به یک متد غیر واسط با یک گیرنده مقدار با استفاده از اشارهگر، به طور خودکار آن اشارهگر: را از ارجاع خارج میکند pt.Mv که معادل است (*pt).Mv.
func(float32) float32همانند فراخوانیهای متد، ارجاع به یک متد غیر واسط با یک گیرنده اشارهگر با استفاده از یک مقدار آدرسپذیر، به طور خودکار آدرس آن مقدار را میگیرد: t.Mp معادل است با (&t).Mp.
اگرچه مثالهای بالا از انواع غیر رابطی استفاده میکنند، اما ایجاد مقدار متد از مقداری از نوع رابط نیز قانونی است.
f := t.Mv; f(7) // like t.Mv(7)
f := pt.Mp; f(7) // like pt.Mp(7)
f := pt.Mv; f(7) // like (*pt).Mv(7)
f := t.Mp; f(7) // like (&t).Mp(7)
f := makeT().Mp // invalid: result of makeT() is not addressable
var i interface { M(int) } = myVal
f := i.M; f(7) // like i.M(7)a[x]
نشان دهنده عنصر آرایه، اشاره گر به آرایه، slice، رشته یا map ای است a که توسط اندیس گذاری شده است x. مقدار به ترتیب کلید اندیس یا mapx نامیده می شود. قوانین زیر اعمال می شود:
ایندکس x باید یک ثابت بدون نوع باشد، یا نوع آن باید یک عدد صحیح یا یک پارامتر نوع باشد که مجموعه نوع آن فقط شامل انواع صحیح است. یک اندیس ثابت باید غیر منفی و قابل نمایش توسط مقداری از نوع باشد.int به یک اندیس ثابت که نوع آن مشخص نشده است، نوع داده میشود.int
یک شاخص ثابت باید در محدوده باشد
x نوع باید به نوع کلید قابل انتساب باشد.M
نوع عناصر همه انواع موجود در P مجموعه نوع باید یکسان باشد. در این زمینه، نوع عنصر یک نوع رشته است byte. اگر یک نوع map در مجموعه نوعهای وجود داشته باشد P، همه نوعهای موجود در آن مجموعه نوع باید از نوع map باشند و انواع کلید مربوطه باید همگی یکسان باشند. a[x] عنصر آرایه، slice یا رشته در اندیس x، یا عنصر map با کلید x آرگومان نوع است که P با آن نمونهسازی میشود، و نوع آن a[x] نوع انواع عناصر (یکسان) است.
یک عبارت شاخص روی map ی a از نوع که map[K]V در یک دستور انتساب یا مقداردهی اولیه فرم ویژه استفاده میشود.
یک مقدار بولی بدون نوع اضافی را برمیگرداند. مقدار آن در صورتی ok است true که کلید x در map وجود داشته باشد و false در غیر این صورت.
اختصاص دادن به یک عنصر از یک nilmap باعث ایجاد panic در زمان اجرا میشود.
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]عبارات slice ی، یک زیررشته یا slice از یک رشته، آرایه، اشارهگر به آرایه یا عملوند slice ی میسازند. دو نوع وجود دارد: یک شکل ساده که حد پایین و بالا را مشخص میکند، و یک شکل کامل که حد ظرفیت را نیز مشخص میکند.
اگر نوع عملوند یک پارامتر نوع باشد، مگر اینکه مجموعه نوع آن شامل انواع رشتهای باشد، همه انواع موجود در مجموعه نوع باید نوع زیربنایی یکسانی داشته باشند و عبارت slice باید برای یک عملوند از آن نوع معتبر باشد. اگر مجموعه نوع شامل انواع رشتهای باشد، ممکن است شامل sliceهای بایت با نوع زیربنایی نیز باشد []byte. در این حالت، عبارت slice باید برای یک عملوند از نوع معتبر باشد string.
یک زیررشته یا slice میسازد. اندیسها low و high انتخاب میکنند که کدام عناصر عملوند a در نتیجه ظاهر شوند. نتیجه دارای اندیسهایی است که از ۰ شروع میشوند و طولی برابر با high - دارند low. پس از slice آرایه a
a[low : high]
برای راحتی، میتوان هر یک از اندیسها را حذف کرد. low اندیس ناموجود به صورت پیشفرض برابر با صفر است؛ high اندیس ناموجود به صورت پیشفرض برابر با طول عملوند slice داده شده است:
a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]
برای آرایهها یا رشتهها، شاخصها در محدوده هستند اگر 0<= low<= high<=، در غیر این صورت خارج از محدوده len(a) هستند. برای sliceها، حد بالای شاخص، ظرفیت slice است نه طول. یک شاخص ثابت باید غیرمنفی باشد و با مقداری از نوع قابل نمایش باشد؛ برای آرایهها یا رشتههای ثابت، شاخصهای ثابت نیز باید در محدوده باشند. اگر هر دو شاخص ثابت باشند، باید شرایط را برآورده کنند. اگر شاخصها در زمان اجرا خارج از محدوده باشند، یک panic زمان اجرا رخ میدهد.cap(a)intlow <= high
s[0] == 2
s[1] == 3
s[2] == 4
به جز رشتههای بدون نوع، اگر عملوند slice خورده یک رشته یا slice باشد، نتیجه عملیات slice یک مقدار غیر ثابت از همان نوع عملوند است. برای عملوندهای رشتهای بدون نوع، نتیجه یک مقدار غیر ثابت از نوع است string. اگر عملوند slice خورده یک آرایه باشد، باید آدرسپذیر باشد و نتیجه عملیات slice، slice ی با همان نوع عنصر آرایه است.
a[2:] // same as a[2 : len(a)]
a[:3] // same as a[0 : 3]
a[:] // same as a[0 : len(a)]
اگر عملوند slice داده شده از یک عبارت slice معتبر، یک nilslice باشد، نتیجه نیز یک nilslice خواهد بود. در غیر این صورت، اگر نتیجه یک slice باشد، آرایه زیرین خود را با عملوند به اشتراک میگذارد.
var a [10]int
s1 := a[3:7] // underlying array of s1 is array a; &s1[2] == &a[5]
s2 := s1[1:4] // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &a[5]
s2[1] = 42 // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array element
var s []int
s3 := s[:0] // s3 == nilیک slice از همان نوع و با همان طول و عناصر عبارت slice ساده میسازد a[low: high]. علاوه بر این، ظرفیت slice حاصل را با تنظیم آن روی کنترل میکند max - low. فقط میتوان اندیس اول را حذف کرد؛ به طور پیشفرض روی 0 است. پس از slice آرایه a
a[low : high : max]
در مورد عبارات slice ساده، اگر a یک اشارهگر به یک آرایه باشد، a[low: high: max] مخفف for است (*a)[low: high: max]. اگر عملوند slice داده شده یک آرایه باشد، باید آدرسپذیر باشد.
a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]
شاخصها در محدوده هستند اگر، در غیر این صورت خارج از محدوده0 <= low <= high <= max <= cap(a) هستند. یک شاخص ثابت باید غیر منفی و قابل نمایش با مقداری از نوع باشد؛ برای آرایهها، شاخصهای ثابت نیز باید در محدوده باشند. اگر چندین شاخص ثابت باشند، ثابتهایی که وجود دارند باید نسبت به یکدیگر در محدوده باشند. اگر شاخصها در زمان اجرا خارج از محدوده باشند، یک panic زمان اجرا رخ میدهد.int
t[0] == 2
t[1] == 3
ایکس.(تی) ادعا میکند که x نیست nil و مقدار ذخیره شده در آن x از نوع است T. این نمادگذاری، ادعای نوع x.(T) نامیده میشود.
x.(T)
به طور دقیقتر، اگر T یک نوع رابط نباشد، x.(T) ادعا میکند که نوع پویای با نوع یکسان x است. در این حالت، باید نوع (رابط) را پیادهسازی کند؛ در غیر این صورت، ادعای نوع نامعتبر است زیرا امکان ذخیره مقداری از نوع وجود ندارد. اگر یک نوع رابط باشد، ادعا میکند که نوع پویای رابط را پیادهسازی میکند.TTxxTTx.(T)x T
اگر عبارت نوع برقرار باشد، مقدار عبارت، مقدار ذخیره شده در x و نوع آن است T. اگر عبارت نوع نادرست باشد، یک خطای زمان اجرا رخ میدهد. به عبارت دیگر، اگرچه نوع پویای "of" x فقط در زمان اجرا مشخص است، اما نوع آن در یک برنامه صحیح x.(T) مشخص است.T
یک نوع ادعا که در یک دستور انتساب یا مقداردهی اولیه فرم خاص استفاده میشود.
var x interface{} = 7 // x has dynamic type int and value 7
i := x.(int) // i has type int and value 7
type I interface { m() }
func f(y I) {
s := y.(string) // illegal: string does not implement I (missing method m)
r := y.(io.Reader) // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
…
}یک مقدار بولی بدون نوع اضافی را برمیگرداند. اگر ادعا برقرار باشد، مقدار okis است. در غیر این صورت، is است و مقدار of برابر با مقدار صفر برای نوع است. در این حالت هیچ گونه خطای زمان اجرا رخ نمیدهد.truefalsevT
v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // dynamic types of v and ok are T and boolf(a1, a2, … an)
f(a1، a2، … an) فراخوانیها f با آرگومانها a1, a2, … an. به جز یک مورد خاص، آرگومانها باید عبارات تک مقداری باشند که به انواع پارامتر قابل انتساب باشند F و قبل از فراخوانی تابع ارزیابی شوند. نوع عبارت، نوع نتیجه است F. فراخوانی یک متد مشابه است، اما خود متد به عنوان یک انتخابگر بر اساس مقداری از نوع گیرنده برای متد مشخص میشود.
math.Atan2(x, y) // function call
var pt *Point
pt.Scale(3.5) // method call with receiver ptاگر نشان دهنده یک تابع عمومی باشد، قبل از اینکه بتوان آن را فراخوانی کرد یا به عنوان مقدار تابع استفاده کرد، f باید نمونهسازی شود.
اگر نوع f یک پارامتر نوع باشد، همه انواع موجود در مجموعه نوع آن باید نوع زیربنایی یکسانی داشته باشند که باید یک نوع تابع باشد و فراخوانی تابع باید برای آن نوع معتبر باشد.
در یک فراخوانی تابع، مقدار تابع و آرگومانها به ترتیب معمول ارزیابی میشوند. پس از ارزیابی، فضای ذخیرهسازی جدیدی برای متغیرهای تابع اختصاص داده میشود که شامل پارامترها و نتایج آن است. سپس، آرگومانهای فراخوانی به تابع ارسال میشوند، به این معنی که به پارامترهای تابع مربوطه خود اختصاص داده میشوند و تابع فراخوانی شده شروع به اجرا میکند. پارامترهای برگشتی تابع هنگام بازگشت تابع به فراخوانی کننده ارسال میشوند.
فراخوانی nil مقدار یک تابع باعث ایجاد یک اختلال در زمان اجرا میشود.
به عنوان یک مورد خاص، اگر مقادیر برگشتی یک تابع یا متد g از نظر تعداد برابر باشند و به صورت جداگانه قابل تخصیص به پارامترهای تابع یا متد دیگری باشند f، آنگاه فراخوانی پس از ارسال مقادیر برگشتی به پارامترهای به ترتیب فراخوانی میشود. فراخوانی نباید شامل هیچ پارامتری غیر از فراخوانی باشد و باید حداقل یک مقدار برگشتی داشته باشد. اگر پارامتر نهایی داشته باشد، مقادیر برگشتی که پس از تخصیص پارامترهای معمولی باقی میمانند، به آن اختصاص داده میشود.f(g(parameters_of_g))fgffggf...g
func Split(s string, pos int) (string, string) {
return s[0:pos], s[pos:]
}
func Join(s, t string) string {
return s + t
}
if Join(Split(value, len(value)/2)) != value {
log.Panic("test fails")
}یک فراخوانی متد x.m() در صورتی معتبر است که مجموعه متد (نوع) x شامل باشد m و لیست آرگومانها را بتوان به لیست پارامترهای اختصاص داد m. اگر قابل آدرسدهی x باشد و مجموعه متد شامل باشد، به صورت خلاصه برای موارد زیر است:&xmx.m()(&x).m()
var p Point
p.Scale(3.5)مقیاس (3.5) هیچ نوع متد مشخصی وجود ندارد و هیچ لفظ متدی هم وجود ندارد.
اگر متغیر با پارامتر نهایی از نوع f باشد، آنگاه درون نوعِ معادل با نوع است. اگر بدون هیچ آرگومان واقعی برای فراخوانی شود، مقدار ارسالی به آن است. در غیر این صورت، مقدار ارسالی یک slice جدید از نوع با یک آرایه جدید است که عناصر متوالی آن آرگومانهای واقعی هستند که همه باید به آنها اختصاص داده شوند. بنابراین، طول و ظرفیت slice، تعداد آرگومانهای متصل به آن است و ممکن است برای هر سایت فراخوانی متفاوت باشد.p...Tfp[]Tfppnil[]TTp
با توجه به تابع و فراخوانیها
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")احوالپرسی ("هیچکس") احوالپرسی("سلام:"، "جو"، "آنا"، "آیلین")
اگر آرگومان آخر قابل انتساب به یک نوع slice باشد []T و پس از آن...، قرار گیرد، بدون تغییر به عنوان مقدار یک...T پارامتر ارسال میشود. در این حالت، slice جدیدی ایجاد نمیشود.
با توجه به slice s و تماس
s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)
احوالپرسی ("خداحافظی:", s...) درون Greeting، who همان مقداری را خواهد داشت که s با همان آرایهی زیرین دارد.
یک تابع یا نوع عمومی با جایگزینی آرگومانهای نوع به جای پارامترهای نوع، نمونهسازی میشود [Go 1.18]. نمونهسازی در دو مرحله انجام میشود:
هر آرگومان نوع در اعلان عمومی با پارامتر نوع متناظر خود جایگزین میشود. این جایگزینی در کل تابع یا اعلان نوع، از جمله خود لیست پارامتر نوع و هر نوع موجود در آن لیست، اتفاق میافتد. پس از جایگزینی، هر آرگومان نوع باید محدودیت (در صورت لزوم، نمونهسازی شده) پارامتر نوع مربوطه را برآورده کند. در غیر این صورت، نمونهسازی با شکست مواجه میشود. نمونهسازی یک نوع منجر به یک نوع نامگذاری شده غیر عمومی جدید میشود؛ نمونهسازی یک تابع، یک تابع غیر عمومی جدید تولید میکند.
پارامتر نوع، آرگومانهای نوع را پس از جایگزینی لیست میکند
type parameter list type arguments after substitution
[P any] int int satisfies any
[S ~[]E, E any] []int, int []int satisfies ~[]int, int satisfies any
[P io.Writer] string illegal: string doesn't satisfy io.Writer
[P comparable] any any satisfies (but does not implement) comparableهنگام استفاده از یک تابع عمومی، آرگومانهای نوع ممکن است به صراحت ارائه شوند، یا ممکن است به طور جزئی یا کامل از زمینهای که تابع در آن استفاده میشود، استنباط شوند. در صورتی که بتوان آنها را استنباط کرد، لیست آرگومانهای نوع ممکن است به طور کامل حذف شوند اگر تابع:
با آرگومانهای معمولی فراخوانی میشود، به متغیری با نوع شناخته شده اختصاص داده شده است به عنوان آرگومان به تابع دیگری ارسال شود، یا در نتیجه برگشت. در تمام موارد دیگر، یک لیست آرگومان نوع (احتمالاً جزئی) باید وجود داشته باشد. اگر لیست آرگومان نوع وجود ندارد یا جزئی است، تمام آرگومانهای نوع مفقود شده باید از زمینهای که تابع در آن استفاده میشود، قابل استنباط باشند.
یک لیست آرگومان نوع جزئی نمیتواند خالی باشد؛ حداقل اولین آرگومان باید وجود داشته باشد. این لیست پیشوندی از لیست کامل آرگومانهای نوع است و آرگومانهای باقیمانده را برای استنباط باقی میگذارد. به طور کلی، آرگومانهای نوع را میتوان از "راست به چپ" حذف کرد.
// sum returns the sum (concatenation, for strings) of its arguments.
func sum[T ~int | ~float64 | ~string](x... T) T { … }
x := sum // illegal: the type of x is unknown
intSum := sum[int] // intSum has type func(x... int) int
a := intSum(2, 3) // a has value 5 of type int
b := sum[float64](2.0, 3) // b has value 5.0 of type float64
c := sum(b, -1) // c has value 4.0 of type float64
type sumFunc func(x... string) string
var f sumFunc = sum // same as var f sumFunc = sum[string]
f = sum // same as f = sum[string]func apply[S ~[]E, E any](s S, f func(E) E) S { … }
f0 := apply[] // illegal: type argument list cannot be empty
f1 := apply[[]int] // type argument for S explicitly provided, type argument for E inferred
f2 := apply[[]string, string] // both type arguments explicitly provided
var bytes []byte
r := apply(bytes, func(byte) byte { … }) // both type arguments inferred from the function argumentsاستفاده از یک تابع عمومی ممکن است برخی یا همه آرگومانهای نوع را حذف کند، اگر بتوان آنها را از زمینهای که تابع در آن استفاده میشود، از جمله محدودیتهای پارامترهای نوع تابع، استنباط کرد. استنباط نوع در صورتی موفق میشود که بتواند آرگومانهای نوع از دست رفته را استنباط کند و نمونهسازی با آرگومانهای نوع استنباط شده موفقیتآمیز باشد. در غیر این صورت، استنباط نوع با شکست مواجه میشود و برنامه نامعتبر است.
استنتاج نوع از روابط نوع بین جفتهای نوع برای استنتاج استفاده میکند: برای مثال، یک آرگومان تابع باید به پارامتر تابع مربوطهاش قابل انتساب باشد؛ این امر رابطهای بین نوع آرگومان و نوع پارامتر برقرار میکند. اگر هر یک از این دو نوع شامل پارامترهای نوع باشند، استنتاج نوع به دنبال آرگومانهای نوع میگردد تا پارامترهای نوع را با آنها جایگزین کند به طوری که رابطه انتسابپذیری برقرار باشد. به طور مشابه، استنتاج نوع از این واقعیت استفاده میکند که یک آرگومان نوع باید محدودیت پارامتر نوع مربوطهاش را برآورده کند.
هر جفت از این نوعهای منطبق، متناظر با یک معادله نوع شامل یک یا چند پارامتر نوع، از یک یا احتمالاً چند تابع عمومی است. استنباط آرگومانهای نوع گمشده به معنای حل مجموعه حاصل از معادلات نوع برای پارامترهای نوع مربوطه است.
متغیر s نوع Slice باید قابل انتساب به نوع پارامتر تابع باشد S تا برنامه معتبر باشد. برای کاهش پیچیدگی، استنتاج نوع، جهت انتسابها را نادیده میگیرد، بنابراین رابطه نوع بین Slice و S را میتوان از طریق معادله نوع (متقارن) بیان کرد (یا به همین ترتیب)، که در آن نشان میدهد که انواع LHS و RHS باید طبق قوانین انتسابپذیری مطابقت داشته باشند (برای جزئیات به بخش یکسانسازی نوع مراجعه کنید). به طور مشابه، پارامتر نوع باید محدودیت خود را برآورده کند. این را میتوان به صورت زیر بیان کرد: که در آن مخفف " محدودیت را برآورده میکند " است. این مشاهدات منجر به مجموعهای از دو معادله میشود:Slice ≡A SS ≡A SliceA≡AS~[]ES ≡C ~[]EX ≡C YXY
// dedup returns a copy of the argument slice with any duplicate entries removed.
func dedup[S ~[]E, E comparable](S) S { … }
type Slice []int
var s Slice
s = dedup(s) // same as s = dedup[Slice, int](s)که اکنون میتوان آن را برای پارامترهای نوع S و حل کرد E. از (1) یک کامپایلر میتواند استنباط کند که آرگومان نوع برای Sis است Slice. به طور مشابه، از آنجا که نوع اصلی Sliceis []int و []intmust []E با محدودیت مطابقت دارد، یک کامپایلر میتواند استنباط کند که Emust be است int. بنابراین، برای این دو معادله، استنتاج نوع استنتاج میکند.
Slice ≡A S (1)
S ≡C ~[]E (2)
با توجه به مجموعهای از معادلات نوع، پارامترهای نوع که باید حل شوند، پارامترهای نوع توابعی هستند که نیاز به نمونهسازی دارند و هیچ آرگومان نوع صریحی برای آنها ارائه نشده است. این پارامترهای نوع، پارامترهای نوع مقید نامیده میشوند. به عنوان مثال، در dedup مثال بالا، پارامترهای نوع S و E مقید به هستند dedup. یک آرگومان برای فراخوانی یک تابع عمومی ممکن است خود یک تابع عمومی باشد. پارامترهای نوع آن تابع در مجموعه پارامترهای نوع مقید گنجانده شدهاند. انواع آرگومانهای تابع ممکن است حاوی پارامترهای نوع از توابع دیگر باشند (مانند یک تابع عمومی که یک فراخوانی تابع را در بر میگیرد). آن پارامترهای نوع ممکن است در معادلات نوع نیز ظاهر شوند، اما در آن زمینه مقید نیستند. معادلات نوع همیشه فقط برای پارامترهای نوع مقید حل میشوند.
S ➞ Slice
E ➞ int
استنتاج نوع، فراخوانی توابع عمومی و انتساب توابع عمومی به متغیرهای (به طور صریح تابع-نوع) را پشتیبانی میکند. این شامل ارسال توابع عمومی به عنوان آرگومان به توابع دیگر (احتمالاً همچنین توابع عمومی) و بازگرداندن توابع عمومی به عنوان نتایج است. استنتاج نوع بر روی مجموعهای از معادلات خاص برای هر یک از این موارد عمل میکند. معادلات به شرح زیر هستند (لیست آرگومانهای نوع برای وضوح حذف شدهاند):
برای فراخوانی تابعی که در آن یا آرگومان تابع یک تابع عمومی است: هر جفت آرگومان و پارامترهای تابع مربوطه که در آن یک ثابت بدون نوع نیست، یک معادله تولید میکند. اگر یک ثابت بدون نوع باشد، و یک پارامتر از نوع مقید باشد، این جفت به طور جداگانه از معادلات نوع جمعآوری میشوند.f(a0, a1, …)fai
برای یک دستور return return …, f, … که در آن f یک تابع عمومی به عنوان نتیجه به یک متغیر نتیجه (غیر عمومی) r از نوع تابع برگردانده میشود:.
علاوه بر این، هر پارامتر نوع و محدودیت نوع مربوطه، معادله نوع را به دست میدهد.PkCkPk ≡C Ck
استنتاج نوع، قبل از در نظر گرفتن ثابتهای بدون نوع، به اطلاعات نوع بهدستآمده از عملوندهای نوعدار اولویت میدهد. بنابراین، استنتاج در دو مرحله انجام میشود:
معادلات نوع برای پارامترهای نوع مقید با استفاده از یکسانسازی نوع حل میشوند. اگر یکسانسازی ناموفق باشد، استنتاج نوع نیز ناموفق خواهد بود.
برای هر پارامتر نوع مقید که هنوز هیچ آرگومانی برای آن استنباط نشده است و یک یا چند جفت با همان پارامتر نوع برای آن جمعآوری شده است، نوع ثابت ثابتها را در تمام آن جفتها به همان روشی که برای عبارات ثابت استفاده میشود، تعیین کنید. آرگومان نوع برای، نوع پیشفرض برای نوع ثابت تعیین شده است. اگر نوع ثابتی به دلیل انواع ثابت متناقض قابل تعیین نباشد، استنباط نوع با شکست مواجه میشود.Pk(cj, Pk)cjPk
اگر همه آرگومانهای نوع پس از این دو مرحله پیدا نشده باشند، استنتاج نوع با شکست مواجه میشود.
Pk ➞ Ak
اگر دو مرحله موفقیتآمیز باشند، استنتاج نوع، یک آرگومان نوع برای هر پارامتر نوع مقید تعیین میکند:
یک آرگومان نوع میتواند یک نوع مرکب باشد که شامل پارامترهای نوع مقید دیگری به عنوان انواع عنصر باشد (یا حتی فقط یک پارامتر نوع مقید دیگر باشد). در یک فرآیند سادهسازی مکرر، پارامترهای نوع مقید در هر آرگومان نوع با آرگومانهای نوع مربوطه برای آن پارامترهای نوع جایگزین میشوند تا زمانی که هر آرگومان نوع عاری از پارامترهای نوع مقید شود.AkPk
اگر آرگومانهای نوع شامل ارجاعات چرخهای به خودشان از طریق پارامترهای نوع مقید باشند، سادهسازی و در نتیجه استنتاج نوع با شکست مواجه میشود. در غیر این صورت، استنتاج نوع موفق میشود.
استنتاج نوع، معادلات نوع را از طریق یکسانسازی نوع حل میکند. یکسانسازی نوع به صورت بازگشتی انواع LHS و RHS یک معادله را مقایسه میکند، که در آن هر دو نوع ممکن است پارامترهای نوع مقید باشند یا شامل آنها باشند، و به دنبال آرگومانهای نوع برای آن پارامترهای نوع میگردد به طوری که LHS و RHS با هم مطابقت داشته باشند (بسته به زمینه، یکسان یا از نظر انتساب سازگار شوند). برای این منظور، استنتاج نوع، mapای از پارامترهای نوع مقید را به آرگومانهای نوع استنباط شده نگه میدارد. این map در طول یکسانسازی نوع بررسی و بهروزرسانی میشود. در ابتدا، پارامترهای نوع مقید شناخته شده هستند اما map خالی است. در طول یکسانسازی نوع، اگر یک آرگومان نوع جدید A استنباط شود، map مربوطه P ➞ A از پارامتر نوع به آرگومان به map اضافه میشود. برعکس، هنگام مقایسه انواع، یک آرگومان نوع شناخته شده (آرگومان نوعی که برای آن یک ورودی map از قبل وجود دارد) جای پارامتر نوع مربوطه را میگیرد. با پیشرفت استنتاج نوع، map بیشتر و بیشتر پر میشود تا زمانی که همه معادلات در نظر گرفته شوند، یا تا زمانی که یکسانسازی شکست بخورد. استنتاج نوع در صورتی موفق میشود که هیچ مرحله یکسانسازی شکست نخورد و map برای هر پارامتر نوع یک ورودی داشته باشد.
استنتاج نوع با یک map خالی شروع میشود. یکسانسازی ابتدا ساختار سطح بالای انواع LHS و RHS را مقایسه میکند. هر دو آرایههایی با طول یکسان هستند؛ اگر انواع عناصر یکسان شوند، یکسان میشوند. هر دو نوع عنصر ساختار هستند؛ اگر تعداد فیلدهای یکسانی با نامهای یکسان داشته باشند و اگر انواع فیلدها یکسان شوند، یکسان میشوند. آرگومان نوع for P هنوز مشخص نیست (هیچ ورودی map ی وجود ندارد)، بنابراین یکپارچهسازی Pwith stringmap را P ➞ string به map اضافه میکند. یکسانسازی انواع فیلد list نیاز به یکسانسازی []Pand []string و در نتیجه Pand دارد string. از آنجایی که آرگومان نوع for P در این مرحله مشخص است (یک ورودی map برای for وجود دارد P)، آرگومان نوع آن string جای P. را میگیرد و از آنجایی که string با یکسان است string، این مرحله یکسانسازی نیز با موفقیت انجام میشود. یکسانسازی LHS و RHS معادله اکنون به پایان رسیده است. استنتاج نوع موفق میشود زیرا فقط یک معادله نوع وجود دارد، هیچ مرحله یکسانسازی با شکست مواجه نشده و map به طور کامل پر شده است.
[10]struct{ elem P, list []P } ≡A [10]struct{ elem string; list []string }یکسانسازی بسته به اینکه دو نوع باید یکسان، سازگار با تخصیص یا فقط از نظر ساختاری برابر باشند، از ترکیبی از یکسانسازی دقیق و آزاد استفاده میکند. قوانین یکسانسازی نوع مربوطه به تفصیل در پیوست توضیح داده شده است.
برای معادلهای به شکل، که در آن و هستند، انواعی هستند که در یک انتساب (شامل ارسال پارامتر و دستورات بازگشتی) دخیل هستند، ساختارهای نوع سطح بالا میتوانند به طور آزادانه یکسانسازی شوند، اما انواع عناصر باید دقیقاً یکسانسازی شوند و با قوانین انتسابها مطابقت داشته باشند.X ≡A YXY
برای معادلهای به فرم، که در آن یک پارامتر نوع و محدودیت متناظر با آن است، قوانین یکسانسازی کمی پیچیدهتر هستند:P ≡C CPC
اگر همه انواع در Ctype set دارای نوع زیربنایی یکسانی باشند U، و P یک آرگومان نوع شناخته شده داشته باشند A، U و A باید به طور آزادانه یکسانسازی شوند. به طور مشابه، اگر همه انواع موجود در C مجموعه نوع 's، انواع کانال با نوع عنصر یکسان و جهتهای کانال غیر متناقض باشند و P آرگومان نوع شناخته شدهای داشته باشند A، محدودترین نوع کانال در C مجموعه نوع 's A باید به طور آزادانه یکسانسازی شود. اگر P آرگومان نوع شناختهشدهای نداشته باشد و C دقیقاً شامل یک عبارت نوع باشد T که نوع اصلی (tilde) نباشد، یکسانسازی map را P ➞ T به map اضافه میکند. اگر نوعی که در بالا توضیح داده شد را C نداشته باشد و یک آرگومان نوع شناخته شده داشته باشد، باید تمام متدهای را داشته باشد، در صورت وجود، و انواع متدهای مربوطه باید دقیقاً یکسان شوند.UPAAC هنگام حل معادلات نوع از محدودیتهای نوع، حل یک معادله ممکن است آرگومانهای نوع اضافی را استنتاج کند، که به نوبه خود ممکن است حل معادلات دیگری را که به آن آرگومانهای نوع وابسته هستند، ممکن سازد. استنتاج نوع، یکسانسازی نوع را تا زمانی که آرگومانهای نوع جدید استنتاج شوند، تکرار میکند.
عملگرها، عملوندها را با هم ترکیب کرده و عبارات را تشکیل میدهند.
Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
binary_op = "||" | "&&" | rel_op | add_op | mul_op .
rel_op = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op = "+" | "-" | "|" | "^" .
mul_op = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
unary_op = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .مقایسهها در جای دیگری مورد بحث قرار گرفتهاند. برای سایر عملگرهای دودویی، نوع عملوندها باید یکسان باشد، مگر اینکه عملیات شامل شیفت یا ثابتهای بدون نوع باشد. برای عملیاتی که فقط شامل ثابتها هستند، به بخش عبارات ثابت مراجعه کنید.
به جز عملیات شیفت، اگر یک عملوند ثابت بدون نوع باشد و عملوند دیگر نباشد، ثابت به طور ضمنی به نوع عملوند دیگر تبدیل میشود.
عملوند سمت راست در یک عبارت shift باید از نوع عدد صحیح [Go 1.13] باشد یا یک ثابت بدون نوع باشد که توسط مقداری از نوع قابل نمایش uint باشد. اگر عملوند سمت چپ یک عبارت shift غیر ثابت، یک ثابت بدون نوع باشد، ابتدا به طور ضمنی به نوعی تبدیل میشود که اگر عبارت shift فقط با عملوند سمت چپ آن جایگزین میشد، به خود میگرفت.
var a [1024]byte
var s uint = 33
// The results of the following examples are given for 64-bit ints.
var i = 1<<s // 1 has type int
var j int32 = 1<<s // 1 has type int32; j == 0
var k = uint64(1<<s) // 1 has type uint64; k == 1<<33
var m int = 1.0<<s // 1.0 has type int; m == 1<<33
var n = 1.0<<s == j // 1.0 has type int32; n == true
var o = 1<<s == 2<<s // 1 and 2 have type int; o == false
var p = 1<<s == 1<<33 // 1 has type int; p == true
var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift
var u1 = 1.0<<s != 0 // illegal: 1.0 has type float64, cannot shift
var u2 = 1<<s != 1.0 // illegal: 1 has type float64, cannot shift
var v1 float32 = 1<<s // illegal: 1 has type float32, cannot shift
var v2 = string(1<<s) // illegal: 1 is converted to a string, cannot shift
var w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression; w == 1<<33
var x = a[1.0<<s] // panics: 1.0 has type int, but 1<<33 overflows array bounds
var b = make([]byte, 1.0<<s) // 1.0 has type int; len(b) == 1<<33
// The results of the following examples are given for 32-bit ints,
// which means the shifts will overflow.
var mm int = 1.0<<s // 1.0 has type int; mm == 0
var oo = 1<<s == 2<<s // 1 and 2 have type int; oo == true
var pp = 1<<s == 1<<33 // illegal: 1 has type int, but 1<<33 overflows int
var xx = a[1.0<<s] // 1.0 has type int; xx == a[0]
var bb = make([]byte, 1.0<<s) // 1.0 has type int; len(bb) == 0پنج سطح تقدم برای عملگرهای دودویی وجود دارد. عملگرهای ضرب قویترین تقدم را دارند، و پس از آنها عملگرهای جمع، عملگرهای مقایسه، &&(و منطقی) و در نهایت ||(یا منطقی) قرار دارند:
عملگر اولویت عملگرهای دودویی با اولویت یکسان از چپ به راست مرتبط میشوند. برای مثال، x / y * z با یکسان است (x / y) * z.
Precedence Operator
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||
+x // x
42 + a - b // (42 + a) - b
23 + 3*x[i] // 23 + (3 * x[i])
x <= f() // x <= f()
^a >> b // (^a) >> b
f() || g() // f() || g()
x == y+1 && <-chanInt > 0 // (x == (y+1)) && ((<-chanInt) > 0)
عملگرهای حسابی روی مقادیر عددی اعمال میشوند و نتیجهای از همان نوع عملوند اول را ارائه میدهند. چهار عملگر حسابی استاندارد (+، -، *، /) روی انواع صحیح، اعشاری و مختلط+ اعمال میشوند؛ همچنین برای رشتهها نیز اعمال میشوند. عملگرهای منطقی بیتی و شیفت فقط روی اعداد صحیح اعمال میشوند.
+ sum integers, floats, complex values, strings
- difference integers, floats, complex values
* product integers, floats, complex values
/ quotient integers, floats, complex values
% remainder integers
& bitwise AND integers
| bitwise OR integers
^ bitwise XOR integers
&^ bit clear (AND NOT) integers
<< left shift integer << integer >= 0
>> right shift integer >> integer >= 0
+ جمع اعداد صحیح، اعشاری، مقادیر مختلط، رشتهها - تفاضل اعداد صحیح، اعشاری، مقادیر مختلط * حاصلضرب اعداد صحیح، اعشاری، مقادیر مختلط / خارج قسمت اعداد صحیح، اعشاری، مقادیر مختلط ٪ باقیمانده اعداد صحیح
func dotProduct[F ~float32|~float64](v1, v2 []F) F {
var s F
for i, x := range v1 {
y := v2[i]
s += x * y
}
return s
}& بیتی و اعداد صحیح ^ اعداد صحیح XOR بیتی &^ اعداد صحیح واضح (و نه)
اگر نوع عملوند، یک پارامتر نوع باشد، عملگر باید روی هر نوع در آن مجموعه نوع اعمال شود. عملوندها به عنوان مقادیر آرگومان نوع که پارامتر نوع با آن نمونهسازی شده است، نمایش داده میشوند و عملیات با دقت آن آرگومان نوع محاسبه میشود. به عنوان مثال، تابع زیر را در نظر بگیرید:
حاصلضرب x * y و جمع به ترتیب با دقت یا، بسته به نوع آرگومان برای، s += x * y محاسبه میشوند.float32float64F
با x / yslice به سمت صفر ("تقسیم کوتاه شده").
x = q*y + r and |r| < |y|
تنها استثنا برای این قانون این است که اگر مقسوم x برای نوع صحیح x، منفیترین مقدار باشد، خارج قسمت به دلیل سرریز q = x / -1 مکمل دو عدد صحیح برابر با x(و) است:r = 0
x y x / y x % y
5 3 1 2
-5 3 -1 -2
5 -3 -1 2
-5 -3 1 -2
عدد صحیح ۸ -۱۲۸ عدد صحیح ۱۶ -۳۲۷۶۸ عدد صحیح ۳۲ -۲۱۴۷۴۸۳۶۴۸ اگر مقسوم علیه یک عدد ثابت باشد، نباید صفر باشد. اگر مقسوم علیه در زمان اجرا صفر باشد، یک خطای زمان اجرا رخ میدهد. اگر مقسوم علیه غیر منفی باشد و مقسوم علیه توان ثابتی از ۲ باشد، تقسیم را میتوان با یک شیفت به راست جایگزین کرد و محاسبه باقیمانده را میتوان با یک عملگر بیتی AND جایگزین کرد:
x, q
int8 -128
int16 -32768
int32 -2147483648
int64 -9223372036854775808
عملگرهای شیفت، عملوند چپ را به اندازه تعداد شیفت مشخص شده توسط عملوند راست، که باید غیر منفی باشد، شیفت میدهند. اگر تعداد شیفت در زمان اجرا منفی باشد، یک پانیک زمان اجرا رخ میدهد. عملگرهای شیفت اگر عملوند چپ یک عدد صحیح علامتدار باشد، شیفتهای حسابی و اگر یک عدد صحیح بدون علامت باشد، شیفتهای منطقی را پیادهسازی میکنند. هیچ حد بالایی برای تعداد شیفت وجود ندارد. شیفتها طوری رفتار میکنند که انگار عملوند چپ n برای تعداد شیفت 1 بار شیفت داده میشود n. در نتیجه، x << 1 برابر با x*2 و x >> 1 برابر با است x/2 اما به سمت منفی بینهایت کوتاه میشود.
x x / 4 x % 4 x >> 2 x & 3
11 2 3 2 3
-11 -2 -3 -3 1
+x برابر است با 0 + x منفی کردن -x برابر با 0 - x است مکمل بیتی ^x برابر است با m ^ x که در آن m برابر است با "همه بیتها روی ۱ تنظیم شدهاند" برای x بدون علامت
+x is 0 + x
-x negation is 0 - x
^x bitwise complement is m ^ x with m = "all bits set to 1" for unsigned x
and m = -1 for signed x
برای مقادیر صحیح بدون علامت، عملیات +، -، *، و به پیمانه 2 n<< محاسبه میشوند، که در آن n پهنای بیت نوع عدد صحیح بدون علامت است. به طور کلی، این عملیاتهای صحیح بدون علامت، بیتهای بالا را پس از سرریز دور میریزند و برنامهها ممکن است به "پیچیدن" متکی باشند.
برای اعداد صحیح علامتدار، عملیاتهای +، -، *، /، و <<ممکن است به طور قانونی سرریز شوند و مقدار حاصل وجود دارد و به طور قطعی توسط نمایش عدد صحیح علامتدار، عملیات و عملوندهای آن تعریف میشود. سرریز باعث ایجاد اختلال در زمان اجرا نمیشود. یک کامپایلر ممکن است کد را با فرض عدم وقوع سرریز بهینه نکند. به عنوان مثال، ممکن است فرض نکند که این x < x + 1 همیشه درست است.
برای اعداد اعشاری و مختلط، +x برابر است با x، در حالی که -x نقیض است x. نتیجه تقسیم ممیز شناور یا مختلط بر صفر فراتر از استاندارد IEEE 754 مشخص نشده است؛ اینکه آیا پانیک زمان اجرا رخ میدهد یا خیر، وابسته به پیادهسازی است.
یک پیادهسازی ممکن است چندین عملیات ممیز شناور را در یک عملیات ادغامشده، احتمالاً بین دستورات، ترکیب کند و نتیجهای تولید کند که با مقداری که با اجرا و گرد کردن دستورالعملها به صورت جداگانه به دست میآید، متفاوت باشد. یک تبدیل نوع ممیز شناور صریح، عمل گرد کردن را به دقت نوع هدف انجام میدهد و از ادغامی که آن گرد کردن را نادیده میگیرد، جلوگیری میکند.
برای مثال، برخی معماریها یک دستورالعمل «ضرب و جمع ترکیبی» (FMA) ارائه میدهند که x*y + z بدون گرد کردن نتیجه میانی محاسبه میکند x*y. این مثالها نشان میدهند که چه زمانی یک پیادهسازی Go میتواند از آن دستورالعمل استفاده کند:
// FMA allowed for computing r, because x*y is not explicitly rounded:
r = x*y + z
r = z; r += x*y
t = x*y; r = t + z
*p = x*y; r = *p + z
r = x*y + float64(z)
// FMA disallowed for computing r, because it would omit rounding of x*y:
r = float64(x*y) + z
r = z; r += float64(x*y)
t = float64(x*y); r = t + z
s := "hi" + string(c)
s += " and good bye"
عملگرهای مقایسهای، دو عملوند را با هم مقایسه میکنند و یک مقدار بولی بدون نوع را برمیگردانند.
== equal
!= not equal
< less
<= less or equal
> greater
>= greater or equal
کمتر > بزرگتر در هر مقایسهای، عملوند اول باید قابل انتساب به نوع عملوند دوم باشد، یا برعکس.
انواع بولی قابل مقایسه هستند. دو مقدار بولی در صورتی برابرند که یا هر دو باشند true یا هر دو false. انواع عدد صحیح قابل مقایسه و مرتب هستند. دو مقدار عدد صحیح به روش معمول مقایسه میشوند. انواع اعشاری قابل مقایسه و مرتب هستند. دو مقدار اعشاری طبق تعریف استاندارد IEEE 754 با هم مقایسه میشوند. انواع مختلط قابل مقایسه هستند. دو مقدار مختلط u و v برابرند اگر هر دو real(u) == real(v) و imag(u) == imag(v). انواع رشتهای قابل مقایسه و مرتب هستند. دو مقدار رشتهای به صورت لغوی و بایت به بایت با هم مقایسه میشوند. انواع اشارهگرها قابل مقایسه هستند. دو مقدار اشارهگر در صورتی برابرند که به یک متغیر اشاره کنند یا هر دو مقدار داشته باشند nil. اشارهگرها به متغیرهای متمایز با اندازه صفر ممکن است برابر باشند یا نباشند. انواع کانال قابل مقایسه هستند. دو مقدار کانال در صورتی برابرند که با فراخوانی یکسانی به ایجاد شده باشند make یا هر دو دارای باشند nil. انواع رابطهایی که پارامترهای نوع نیستند، قابل مقایسه هستند. دو مقدار رابط در صورتی برابرند که انواع پویای یکسان و مقادیر پویای برابری داشته باشند یا هر دو دارای مقدار باشند nil. یک مقدار x از نوع غیر رابط X و یک مقدار t از نوع رابط T را میتوان با هم مقایسه کرد اگر نوع X قابل مقایسه باشد و X پیادهسازی کند. اگر نوع پویای با یکسان و مقدار پویای برابر باشد، T آنها برابرند.tXtx انواع ساختارها در صورتی قابل مقایسه هستند که تمام انواع فیلدهای آنها قابل مقایسه باشند. دو مقدار ساختار در صورتی برابر هستند که مقادیر فیلد غیر خالی متناظر آنها برابر باشد. فیلدها به ترتیب مبدا مقایسه میشوند و به محض اینکه مقادیر دو فیلد متفاوت باشند (یا همه فیلدها مقایسه شده باشند)، مقایسه متوقف میشود. انواع آرایهها در صورتی قابل مقایسه هستند که نوع عناصر آرایه آنها قابل مقایسه باشد. دو مقدار آرایه در صورتی برابر هستند که مقادیر عناصر متناظر آنها برابر باشد. عناصر به ترتیب شاخص صعودی مقایسه میشوند و به محض اینکه مقادیر دو عنصر متفاوت شوند (یا همه عناصر مقایسه شوند)، مقایسه متوقف میشود. پارامترهای نوع در صورتی قابل مقایسه هستند که کاملاً قابل مقایسه باشند (به زیر مراجعه کنید). مقایسه دو مقدار رابط با انواع پویای یکسان، در صورتی که آن نوع قابل مقایسه نباشد، باعث ایجاد یک اختلال در زمان اجرا میشود. این رفتار نه تنها در مقایسه مستقیم مقادیر رابط، بلکه در مقایسه آرایههایی از مقادیر رابط یا ساختارها با فیلدهای دارای مقدار رابط نیز صدق میکند.
انواع slice، map و تابع قابل مقایسه نیستند. با این حال، به عنوان یک مورد خاص، مقدار یک slice، map یا تابع را میتوان با شناسه از پیش تعریف شده مقایسه کرد nil. مقایسه مقادیر اشارهگر، کانال و رابط nil نیز مجاز است و از قوانین کلی بالا پیروی میکند.
یک نوع در صورتی کاملاً قابل مقایسه است که قابل مقایسه باشد و نه یک نوع رابط باشد و نه از انواع رابط تشکیل شده باشد. به طور خاص:
انواع بولی، عددی، رشتهای، اشارهگر و کانال کاملاً قابل مقایسه هستند. انواع ساختارها در صورتی کاملاً قابل مقایسه هستند که تمام انواع فیلدهای آنها کاملاً قابل مقایسه باشند. انواع آرایهها در صورتی کاملاً قابل مقایسه هستند که نوع عناصر آرایه آنها کاملاً قابل مقایسه باشد. پارامترهای نوع در صورتی کاملاً قابل مقایسه هستند که همه انواع موجود در مجموعه نوع خود کاملاً قابل مقایسه باشند.
const c = 3 < 4 // c is the untyped boolean constant true
type MyBool bool
var x, y int
var (
// The result of a comparison is an untyped boolean.
// The usual assignment rules apply.
b3 = x == y // b3 has type bool
b4 bool = x == y // b4 has type bool
b5 MyBool = x == y // b5 has type MyBool
)عملگرهای منطقی روی مقادیر بولی اعمال میشوند و نتیجهای از همان نوع عملوندها را ارائه میدهند. عملوند سمت چپ ارزیابی میشود و سپس در صورت وجود شرط، عملوند سمت راست ارزیابی میشود.
&& conditional AND p && q is "if p then q else false"
|| conditional OR p || q is "if p then true else q"
! NOT !p is "not p"
&& شرطی و p && q برابر است با «اگر p آنگاه q در غیر این صورت نادرست است»!نه!p همان «p نیست» است
برای یک عملوند x از نوع T، عملیات آدرسدهی، &x اشارهگری از نوع ایجاد میکند *T. x عملوند باید آدرسپذیر باشد، یعنی یا یک متغیر، اشارهگر غیرمستقیم یا عملیات اندیسگذاری slice ی؛ یا یک انتخابگر فیلد از یک عملوند ساختار آدرسپذیر؛ یا یک عملیات اندیسگذاری آرایه از یک آرایه آدرسپذیر. به عنوان یک استثنا برای الزام آدرسپذیری، میتواند یک لیترال مرکب x (احتمالاً پرانتزگذاری شده) نیز باشد. اگر ارزیابی باعث ایجاد اختلال در زمان اجرا شود، ارزیابی نیز چنین میکند.x&x
برای یک عملوند x از نوع اشارهگر *T، عبارت غیرمستقیم اشارهگر، متغیری از نوع مورد اشاره توسط را *x نشان میدهد. اگر چنین باشد، تلاش برای ارزیابی باعث ایجاد اختلال در زمان اجرا خواهد شد.Txxnil*x
&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)
var x *int = nil
*x // causes a run-time panic
&*x // causes a run-time panicو ایکس *ص
اگر نوع عملوند یک پارامتر نوع باشد، همه انواع موجود در مجموعه نوع آن باید از نوع کانال باشند که عملیات دریافت را مجاز میدانند و همه آنها باید نوع عنصر یکسانی داشته باشند که همان نوع عملیات دریافت است.
v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe // wait until clock pulse and discard received value
عبارت دریافتی که در یک دستور انتساب یا مقداردهی اولیه فرم ویژه استفاده میشود
یک نتیجه بولی بدون نوع اضافی ارائه میدهد که گزارش میدهد آیا ارتباط موفقیتآمیز بوده است یا خیر. مقدار ok اگر true مقدار دریافتی توسط یک عملیات ارسال موفق به کانال تحویل داده شده باشد، یا false اگر مقدار صفر باشد زیرا کانال بسته و خالی است، مقدار آن صفر است.
x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-chیک تبدیل، نوع یک عبارت را به نوعی که توسط تبدیل مشخص شده است، تغییر میدهد. یک تبدیل ممکن است به معنای واقعی کلمه در منبع ظاهر شود، یا ممکن است به طور ضمنی توسط متنی که عبارت در آن ظاهر میشود، بیان شود.
تبدیل صریح، عبارتی به شکل است T(x) که در آن T یک نوع است و x عبارتی است که میتواند به نوع تبدیل شود T.
Conversion = Type "(" Expression [ "," ] ")" .یک مقدار ثابت x را میتوان به نوع تبدیل کرد T اگر با مقداری از قابل نمایش x باشد. به عنوان یک مورد خاص، یک ثابت عدد صحیح را میتوان به طور صریح با استفاده از همان قاعدهای که برای غیر ثابتها استفاده میشود، به یک نوع رشته تبدیل کرد.Txx
*Point(p) // same as *(Point(p))
(*Point)(p) // p is converted to *Point
<-chan int(c) // same as <-(chan int(c))
(<-chan int)(c) // c is converted to <-chan int
func()(x) // function signature func() x
(func())(x) // x is converted to func()
(func() int)(x) // x is converted to func() int
func() int(x) // x is converted to func() int (unambiguous)تبدیل یک ثابت به نوعی که پارامتر نوع نیست، یک ثابت نوعدار (typed constant) ایجاد میکند.
تبدیل یک ثابت به یک پارامتر نوع، یک مقدار غیر ثابت از آن نوع را تولید میکند که این مقدار به عنوان مقدار آرگومان نوع که پارامتر نوع با آن نمونهسازی شده است، نمایش داده میشود. برای مثال، تابع زیر را در نظر بگیرید:
uint(iota) // iota value of type uint
float32(2.718281828) // 2.718281828 of type float32
complex128(1) // 1.0 + 0.0i of type complex128
float32(0.49999999) // 0.5 of type float32
float64(-1e-1000) // 0.0 of type float64
string('x') // "x" of type string
string(0x266c) // "♬" of type string
myString("foo" + "bar") // "foobar" of type myString
string([]byte{'a'}) // not a constant: []byte{'a'} is not a constant
(*int)(nil) // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type
int(1.2) // illegal: 1.2 cannot be represented as an int
string(65.0) // illegal: 65.0 is not an integer constantتبدیل P(1.1) منجر به یک مقدار غیر ثابت از نوع میشود P و این مقدار بسته به نوع آرگومان برای 1.1 به صورت a float32 یا a نمایش داده میشود. بر این اساس، اگر با یک نوع نمونهسازی شود، مقدار عددی عبارت با همان دقت جمع غیر ثابت مربوطه محاسبه خواهد شد.float64fffloat32P(1.1) + 1.2float32
func f[P ~float32|~float64]() {
… P(1.1) …
}یک مقدار غیر ثابت را x میتوان در هر یک از موارد زیر به نوع تبدیل کرد T:
x قابل تخصیص است به T. با نادیده گرفتن تگهای struct (به پایین مراجعه کنید)، x نوع و پارامترهای نوع T نیستند، اما انواع اساسی یکسانی دارند. با نادیده گرفتن تگهای struct (به پایین مراجعه کنید)، x نوع و از نوعهای اشارهگری هستند که نوع نامگذاریشده T نیستند و انواع پایه اشارهگر آنها پارامترهای نوع نیستند، اما انواع زیربنایی یکسانی دارند. x نوع 's است و T هر دو از نوع عدد صحیح یا ممیز شناور هستند. x نوع 's است و T هر دو از نوع مختلط هستند. x یک عدد صحیح یا slice ی از بایتها یا رونها است و T یک نوع رشتهای است. x یک رشته است و Tslice ی از بایتها یا رونها میباشد. x یک slice است، T یک آرایه است [Go 1.20] یا یک اشارهگر به یک آرایه است [Go 1.17]، و انواع slice و آرایه انواع عناصر یکسانی دارند. علاوه بر این، اگر نوع T یا پارامترهای نوع باشند، در صورت اعمال یکی از شرایط زیر، میتوانند به نوع تبدیل شوند:xVxT
هر دو V و T پارامترهای نوع هستند و مقداری از هر نوع در V مجموعه نوع را میتوان به هر نوع در T مجموعه نوع تبدیل کرد. فقط V یک پارامتر نوع است و مقداری از هر نوع در V مجموعه نوع 's میتواند به تبدیل شود T. فقط T یک پارامتر نوع است و میتواند به هر نوع موجود در مجموعه نوع x تبدیل شود.T تگهای ساختار هنگام مقایسه انواع ساختار برای شناسایی به منظور تبدیل، نادیده گرفته میشوند:
قوانین خاصی برای تبدیلهای (غیر ثابت) بین انواع عددی یا به و از یک نوع رشتهای اعمال میشود. این تبدیلها ممکن است نمایش را تغییر دهند x و هزینه زمان اجرا را متحمل شوند. سایر تبدیلها فقط نوع را تغییر میدهند اما نمایش را تغییر نمیدهند x.
هیچ مکانیزم زبانی برای تبدیل بین اشارهگرها و اعداد صحیح وجود ندارد. این بسته unsafe این قابلیت را تحت شرایط محدود پیادهسازی میکند.
type Person struct {
Name string
Address *struct {
Street string
City string
}
}
var data *struct {
Name string `json:"name"`
Address *struct {
Street string `json:"street"`
City string `json:"city"`
} `json:"address"`
}
var person = (*Person)(data) // ignoring tags, the underlying types are identicalهنگام تبدیل یک عدد اعشاری به یک عدد صحیح، کسر حذف میشود (کوتاهسازی به سمت صفر). هنگام تبدیل یک عدد صحیح یا اعشاری به یک نوع اعشاری، یا یک عدد مختلط به یک نوع مختلط دیگر، مقدار حاصل به دقتی که توسط نوع مقصد مشخص شده است، گرد میشود. برای مثال، مقدار یک متغیر x از این نوع float32 ممکن است با استفاده از دقتی اضافی فراتر از یک عدد ۳۲ بیتی IEEE 754 ذخیره شود، اما float32(x) نتیجه گرد کردن x مقدار به دقت ۳۲ بیتی را نشان میدهد. به طور مشابه، x + 0.1 ممکن است از دقتی بیش از ۳۲ بیت استفاده کند، اما float32(x + 0.1) این کار را نمیکند. در تمام تبدیلهای غیرثابت شامل مقادیر ممیز شناور یا مختلط، اگر نوع نتیجه نتواند مقدار را نشان دهد، تبدیل موفق میشود اما مقدار نتیجه وابسته به پیادهسازی است.
تبدیل یک slice از بایتها به نوع رشته، رشتهای را تولید میکند که بایتهای متوالی آن، عناصر slice هستند.
تبدیل یک slice از رونها به یک نوع رشته، رشتهای را ایجاد میکند که حاصل الحاق مقادیر رونهای منفرد تبدیلشده به رشته است.
تایپ myRune rune تبدیل مقداری از نوع رشته به slice ی از نوع بایت، یک slice غیر تهی ایجاد میکند که عناصر متوالی آن، بایتهای رشته هستند. ظرفیت slice حاصل، وابسته به پیادهسازی است و ممکن است بزرگتر از طول slice باشد.
تبدیل مقداری از نوع رشته به slice ی از نوع rune، slice ی حاوی نقاط کد یونیکد رشته را ایجاد میکند. ظرفیت slice حاصل، وابسته به پیادهسازی است و ممکن است بزرگتر از طول slice باشد.
در نهایت، به دلایل تاریخی، یک مقدار صحیح میتواند به یک نوع رشته تبدیل شود. این نوع تبدیل، رشتهای حاوی (احتمالاً چند بایتی) نمایش UTF-8 از نقطه کد یونیکد با مقدار عدد صحیح داده شده را تولید میکند. مقادیر خارج از محدوده نقاط کد یونیکد معتبر به تبدیل میشوند "\uFFFD".
رشته myString را تایپ کنید نکته: این نوع تبدیل ممکن است در نهایت از زبان حذف شود. این go vet ابزار تبدیلهای خاص عدد صحیح به رشته را به عنوان خطاهای احتمالی علامتگذاری میکند. به جای آن باید از توابع کتابخانهای مانند utf8.AppendRuneor utf8.EncodeRune استفاده شود.
تبدیل یک slice به یک آرایه، آرایهای حاوی عناصر آرایهی زیرین آن slice را تولید میکند. به طور مشابه، تبدیل یک slice به یک اشارهگر آرایه، اشارهگری به آرایهی زیرین آن slice تولید میکند. در هر دو حالت، اگر طول slice کمتر از طول آرایه باشد، خطای زمان اجرا رخ میدهد.
s := make([]byte, 2, 4)
a0 := [0]byte(s)
a1 := [1]byte(s[1:]) // a1[0] == s[1]
a2 := [2]byte(s) // a2[0] == s[0]
a4 := [4]byte(s) // panics: len([4]byte) > len(s)
s0 := (*[0]byte)(s) // s0 != nil
s1 := (*[1]byte)(s[1:]) // &s1[0] == &s[1]
s2 := (*[2]byte)(s) // &s2[0] == &s[0]
s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s)
var t []string
t0 := [0]string(t) // ok for nil slice t
t1 := (*[0]string)(t) // t1 == nil
t2 := (*[1]string)(t) // panics: len([1]string) > len(t)
u := make([]byte, 0)
u0 := (*[0]byte)(u) // u0 != nilعبارات ثابت ممکن است فقط شامل عملوندهای ثابت باشند و در زمان کامپایل ارزیابی میشوند.
ثابتهای بولی، عددی و رشتهای بدون نوع میتوانند به عنوان عملوند استفاده شوند، هر جا که استفاده از عملوندی از نوع بولی، عددی یا رشتهای به ترتیب مجاز باشد.
یک مقایسه ثابت همیشه یک ثابت بولی بدون نوع را نتیجه میدهد. اگر عملوند چپ یک عبارت shift ثابت، یک ثابت بدون نوع باشد، نتیجه یک ثابت صحیح است؛ در غیر این صورت، ثابتی از همان نوع عملوند چپ است که باید از نوع صحیح باشد.
هر عملیات دیگری روی ثابتهای بدون نوع، منجر به یک ثابت بدون نوع از همان نوع میشود؛ یعنی یک ثابت بولی، عدد صحیح، ممیز شناور، مختلط یا رشتهای. اگر عملوندهای بدون نوع یک عملیات دودویی (به غیر از شیفت) از نوعهای مختلفی باشند، نتیجه از نوع عملوندی است که بعداً در این لیست ظاهر میشود: عدد صحیح، رون، ممیز شناور، مختلط. به عنوان مثال، یک ثابت عدد صحیح بدون نوع تقسیم بر یک ثابت مختلط بدون نوع، یک ثابت مختلط بدون نوع را به دست میدهد.
const a = 2 + 3.0 // a == 5.0 (untyped floating-point constant)
const b = 15 / 4 // b == 3 (untyped integer constant)
const c = 15 / 4.0 // c == 3.75 (untyped floating-point constant)
const Θ float64 = 3/2 // Θ == 1.0 (type float64, 3/2 is integer division)
const Π float64 = 3/2. // Π == 1.5 (type float64, 3/2. is float division)
const d = 1 << 3.0 // d == 8 (untyped integer constant)
const e = 1.0 << 3 // e == 8 (untyped integer constant)
const f = int32(1) << 33 // illegal (constant 8589934592 overflows int32)
const g = float64(2) >> 1 // illegal (float64(2) is a typed floating-point constant)
const h = "foo" > "bar" // h == true (untyped boolean constant)
const j = true // j == true (untyped boolean constant)
const k = 'w' + 1 // k == 'x' (untyped rune constant)
const l = "hi" // l == "hi" (untyped string constant)
const m = string(k) // m == "x" (type string)
const Σ = 1 - 0.707i // (untyped complex constant)
const Δ = Σ + 2.0e-4 // (untyped complex constant)
const Φ = iota*1i - 1/1i // (untyped complex constant)اعمال تابع داخلی complex روی ثابتهای عدد صحیح، رون یا ممیز شناور بدون نوع، یک ثابت مختلط بدون نوع تولید میکند.
const ic = complex(0, c) // ic == 3.75i (untyped complex constant)
const iΘ = complex(0, Θ) // iΘ == 1i (type complex128)عبارات ثابت همیشه دقیقاً ارزیابی میشوند؛ مقادیر میانی و خود ثابتها ممکن است به دقتی بسیار بزرگتر از آنچه توسط هر نوع از پیش تعریف شده در زبان پشتیبانی میشود، نیاز داشته باشند. موارد زیر اعلانهای قانونی هستند:
const Huge = 1 << 100 // Huge == 1267650600228229401496703205376 (untyped integer constant)
const Four int8 = Huge >> 98 // Four == 4 (type int8)مقسوم علیه یک عمل تقسیم یا باقیمانده ثابت نباید صفر باشد:
3.14 / 0.0 // illegal: division by zero
مقادیر ثابتهای نوعگذاری شده باید همیشه به طور دقیق توسط مقادیر نوع ثابت قابل نمایش باشند. عبارات ثابت زیر غیرمجاز هستند:
uint(-1) // -1 cannot be represented as a uint
int(3.14) // 3.14 cannot be represented as an int
int64(Huge) // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300 // operand 300 cannot be represented as an int8 (type of Four)
Four * 100 // product 400 cannot be represented as an int8 (type of Four)ماسکی که توسط عملگر مکمل بیتی یگانی استفاده میشود، ^با قانون مربوط به ثابتهای غیرعلامتدار مطابقت دارد: ماسک برای ثابتهای بدون علامت، تماماً ۱ و برای ثابتهای علامتدار و بدون نوع، ۱- است.
^1 // untyped integer constant, equal to -2
uint8(^1) // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1) // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1) // same as int8(-2)
^int8(1) // same as -1 ^ int8(1) = -2
محدودیت پیادهسازی: یک کامپایلر ممکن است هنگام محاسبهی عبارات ممیز شناور بدون نوع یا عبارات ثابت پیچیده، از گرد کردن استفاده کند؛ به محدودیت پیادهسازی در بخش مربوط به ثابتها مراجعه کنید. این گرد کردن ممکن است باعث شود که یک عبارت ثابت ممیز شناور در یک زمینهی عدد صحیح نامعتبر باشد، حتی اگر هنگام محاسبه با استفاده از دقت بینهایت، صحیح باشد و برعکس.
در سطح بسته، وابستگیهای مقداردهی اولیه، ترتیب ارزیابی عبارات مقداردهی اولیه منفرد را در اعلان متغیرها تعیین میکنند. در غیر این صورت، هنگام ارزیابی عملوندهای یک عبارت، انتساب یا دستور return، تمام فراخوانیهای تابع، فراخوانیهای متد، عملیات دریافت و عملیات منطقی دودویی به ترتیب لغوی از چپ به راست ارزیابی میشوند.
در سطح بسته، وابستگیهای مقداردهی اولیه، قانون چپ به راست را برای عبارات مقداردهی اولیهی منفرد لغو میکنند، اما برای عملوندهای درون هر عبارت این قانون صدق نمیکند:
y[f()], ok = g(z || h(), i()+x[j()], <-c), k()
فراخوانیهای تابع به ترتیب u()، sqr()، v()، f()، v() و اتفاق میافتند g().
a := 1
f := func() int { a++; return a }
x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
m := map[int]int{a: 1, a: 2} // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
n := map[int]int{a: f()} // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specifiedعملیات ممیز شناور درون یک عبارت واحد بر اساس میزان شرکتپذیری عملگرها ارزیابی میشوند. پرانتزهای صریح با لغو کردن میزان شرکتپذیری پیشفرض، بر ارزیابی تأثیر میگذارند. در عبارت، x + (y + z) جمع y + z قبل از جمع انجام میشود x.
var a, b, c = f() + v(), g(), sqr(u()) + v()
func f() int { return c }
func g() int { return a }
func sqr(x int) int { return x*x }
// functions u and v are independent of all other variables and functionsStatement = Declaration | LabeledStmt | SimpleStmt |
GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
DeferStmt .
SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .یک دستور خاتمه دهنده، جریان منظم کنترل در یک بلوک را قطع میکند. دستورات زیر خاتمه دهنده هستند:
یک دستور "return" یا "goto". فراخوانی تابع داخلی panic. بلوکی که در آن لیست دستورات با یک دستور خاتمه دهنده خاتمه مییابد. یک عبارت «اگر» که در آن: شاخه "else" وجود دارد، و هر دو شاخه، دستورات خاتمه دهنده هستند. یک عبارت «for» که در آن: هیچ دستور "break" به دستور "for" اشاره نمیکند، و شرط حلقه وجود ندارد، و عبارت "for" از عبارت محدوده استفاده نمیکند. یک دستور "switch" که در آن: هیچ دستور "break" به دستور "switch" اشاره نمیکند، یک حالت پیشفرض وجود دارد، و این دستور در هر مورد، از جمله پیشفرض، فهرست میکند و با یک دستور خاتمهدهنده یا احتمالاً یک دستور با برچسب "fallthrough" خاتمه مییابد. یک دستور "select" که در آن: هیچ دستور "break" به دستور "select" اشاره نمیکند، و این دستور در هر مورد، از جمله مقدار پیشفرض در صورت وجود، را فهرست میکند و با یک دستور خاتمهدهنده خاتمه مییابد. یک دستور برچسبگذاری شده که یک دستور خاتمهدهنده را برچسبگذاری میکند. تمام دستورات دیگر خاتمه دهنده نیستند.
یک لیست دستور، در صورتی با یک دستور پایانی خاتمه مییابد که لیست خالی نباشد و آخرین دستور غیر خالی آن، دستور پایانی باشد.
عبارت خالی هیچ کاری انجام نمیدهد.
EmptyStmt = .LabeledStmt = Label ":" Statement .
Label = identifier .Error: log.Panic("error encountered")
خطا: log.Panic("خطا رخ داده است")
به استثنای توابع داخلی خاص، فراخوانی توابع و متدها و عملیات دریافت میتوانند در متن دستور ظاهر شوند. چنین دستوراتی میتوانند درون پرانتز قرار گیرند.
ExpressionStmt = Expression .توابع داخلی زیر در متن دستور مجاز نیستند:
append cap complex imag len make new real
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice unsafe.SliceData unsafe.String unsafe.StringData
h(x+y)
f.Close()
<-ch
(<-ch)
len("foo") // illegal if len is the built-in function
اضافه کردن تصویر پیچیده به کلاه، تبدیل آن به واقعیت جدید
یک دستور ارسال، مقداری را روی یک کانال ارسال میکند. عبارت کانال باید از نوع کانال باشد، جهت کانال باید اجازه عملیات ارسال را بدهد و نوع مقداری که قرار است ارسال شود باید به نوع عنصر کانال قابل انتساب باشد.
SendStmt = Channel "<-" Expression .
Channel = Expression .قبل از شروع ارتباط، هم کانال و هم عبارت مقدار ارزیابی میشوند. ارتباط تا زمانی که ارسال بتواند ادامه یابد، مسدود میشود. ارسال در یک کانال بافر نشده میتواند در صورت آماده بودن گیرنده ادامه یابد. ارسال در یک کانال بافر شده میتواند در صورت وجود فضای کافی در بافر، ادامه یابد. ارسال در یک کانال بسته با ایجاد یک وقفه در زمان اجرا ادامه مییابد. ارسال در یک nil کانال برای همیشه مسدود میشود.
ch <- 3 // send value 3 to channel ch
اگر نوع عبارت کانال یک پارامتر نوع باشد، همه انواع موجود در مجموعه نوع آن باید از نوع کانال باشند که عملیات ارسال را مجاز میدانند، همه آنها باید نوع عنصر یکسانی داشته باشند و نوع مقداری که قرار است ارسال شود باید به آن نوع عنصر قابل انتساب باشد.
دستورات انتساب زیر از نظر معنایی معادل هستند:
IncDecStmt = Expression ( "++" | "--" ) .انتساب صورتجلسه IncDec
IncDec statement Assignment
x++ x += 1
x-- x -= 1
یک انتساب، مقدار فعلی ذخیره شده در یک متغیر را با مقدار جدیدی که توسط یک عبارت مشخص میشود، جایگزین میکند. یک دستور انتساب میتواند یک مقدار واحد را به یک متغیر واحد یا چندین مقدار را به تعداد متناظری از متغیرها انتساب دهد.
Assignment = ExpressionList assign_op ExpressionList .
assign_op = [ add_op | mul_op ] "=" .هر عملوند سمت چپ باید آدرسپذیر، یک عبارت اندیس map یا (=فقط برای انتسابها) شناسه خالی باشد. عملوندها میتوانند درون پرانتز قرار گیرند.
x = 1
*p = f()
a[i] = 23
(k) = <-ch // same as: k = <-ch
یک عملیات انتساب x op= y که در آن op یک عملگر حسابی دودویی است، معادل x = x op (y) است اما x فقط یک بار ارزیابی میشود. ساختار op= یک توکن واحد است. در عملیات انتساب، هر دو لیست عبارت چپ و راست باید دقیقاً شامل یک عبارت تک مقداری باشند و عبارت چپ نباید شناسه خالی باشد.
a[i] <<= 2
i &^= 1<<n
یک انتساب چندتایی، عناصر منفرد یک عملیات چند مقداری را به لیستی از متغیرها اختصاص میدهد. دو شکل وجود دارد. در حالت اول، عملوند سمت راست یک عبارت چند مقداری واحد مانند فراخوانی تابع، عملیات کانال یا map یا یک ادعای نوع است. تعداد عملوندهای سمت چپ باید با تعداد مقادیر مطابقت داشته باشد. به عنوان مثال، اگر f تابعی باشد که دو مقدار را برمیگرداند،
x, y = f()
مقدار اول را به x و مقدار دوم را به اختصاص میدهد y. در شکل دوم، تعداد عملوندهای سمت چپ باید برابر با تعداد عبارات سمت راست باشد که هر کدام باید تک مقداری باشند و عبارت n ام سمت راست به عملوند n ام سمت چپ اختصاص داده میشود:
one, two, three = '一', '二', '三'
شناسه خالی راهی برای نادیده گرفتن مقادیر سمت راست در یک انتساب فراهم میکند:
_ = x // evaluate x but ignore it
x, _ = f() // evaluate f() but ignore second result value
این انتساب در دو مرحله انجام میشود. اول، عملوندهای عبارات اندیس و اشارهگرهای غیرمستقیم (از جمله اشارهگرهای غیرمستقیم ضمنی در انتخابگرها) در سمت چپ و عبارات سمت راست، همگی به ترتیب معمول ارزیابی میشوند. دوم، انتسابها به ترتیب از چپ به راست انجام میشوند.
a, b = b, a // exchange a and b
x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2 // set i = 1, x[0] = 2
i = 0
x[i], i = 2, 1 // set x[0] = 2, i = 1
x[0], x[0] = 1, 2 // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)
x[1], x[3] = 4, 5 // set x[1] = 4, then panic setting x[3] = 5.
type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7 // set x[2] = 6, then panic setting p.x = 7
i = 2
x = []int{3, 5, 7}
for i, x[i] = range x { // set i, x[2] = 0, x[0]
break
}
// after this loop, i == 0 and x is []int{3, 5, 3}در انتسابها، هر مقدار باید به نوع عملوندی که به آن انتساب داده شده است، قابل انتساب باشد، با موارد خاص زیر:
هر مقدار تایپی میتواند به شناسه خالی اختصاص داده شود. اگر یک ثابت بدون نوع به متغیری از نوع رابط یا شناسه خالی اختصاص داده شود، ثابت ابتدا به طور ضمنی به نوع پیشفرض خود تبدیل میشود. اگر یک مقدار بولی بدون نوع به متغیری از نوع رابط یا شناسه خالی اختصاص داده شود، ابتدا به طور ضمنی به نوع تبدیل میشود bool. وقتی مقداری به یک متغیر اختصاص داده میشود، فقط دادههایی که در متغیر ذخیره شدهاند جایگزین میشوند. اگر مقدار حاوی یک ارجاع باشد، انتساب، ارجاع را کپی میکند اما از دادههای ارجاعشده (مانند آرایهی اصلی یک slice) کپی تهیه نمیکند.
var s1 = []int{1, 2, 3}
var s2 = s1 // s2 stores the slice descriptor of s1
s1 = s1[:1] // s1's length is 1 but it still shares its underlying array with s2
s2[0] = 42 // setting s2[0] changes s1[0] as well
fmt.Println(s1, s2) // prints [42] [42 2 3]
var m1 = make(map[string]int)
var m2 = m1 // m2 stores the map descriptor of m1
m1["foo"] = 42 // setting m1["foo"] changes m2["foo"] as well
fmt.Println(m2["foo"]) // prints 42دستورات "اگر" اجرای شرطی دو شاخه را بر اساس مقدار یک عبارت بولی مشخص میکنند. اگر عبارت به درست ارزیابی شود، شاخه "اگر" اجرا میشود، در غیر این صورت، در صورت وجود، شاخه "در غیر این صورت" اجرا میشود.
IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .if x > max {
x = max
}
ممکن است قبل از عبارت، یک دستور ساده قرار گیرد که قبل از ارزیابی عبارت اجرا میشود.
if x := f(); x < y {
return x
} else if x > z {
return z
} else {
return y
}دستورات "Switch" امکان اجرای چند طرفه را فراهم میکنند. یک عبارت یا نوع با "case ها"ی داخل "switch" مقایسه میشود تا مشخص شود کدام شاخه اجرا شود.
SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .دو شکل وجود دارد: سوئیچهای عبارت و سوئیچهای نوع. در یک سوئیچ عبارت، موارد شامل عباراتی هستند که با مقدار عبارت سوئیچ مقایسه میشوند. در یک سوئیچ نوع، موارد شامل انواعی هستند که با نوع یک عبارت سوئیچ حاشیهنویسی شده خاص مقایسه میشوند. عبارت سوئیچ دقیقاً یک بار در یک دستور سوئیچ ارزیابی میشود.
در یک عبارت switch، عبارت switch ارزیابی میشود و عبارات case که نیازی به ثابت بودن ندارند، از چپ به راست و از بالا به پایین ارزیابی میشوند؛ اولین موردی که با عبارت switch برابر باشد، اجرای دستورات case مربوطه را آغاز میکند؛ سایر حالات نادیده گرفته میشوند. اگر هیچ موردی مطابقت نداشته باشد و یک حالت "default" وجود داشته باشد، دستورات آن اجرا میشوند. حداکثر یک حالت default میتواند وجود داشته باشد و ممکن است در هر جایی از عبارت "switch" ظاهر شود. یک عبارت switch گم شده معادل مقدار بولی است true.
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .اگر عبارت سوئیچ به یک ثابت بدون نوع ارزیابی شود، ابتدا به طور ضمنی به نوع پیشفرض خود تبدیل میشود. مقدار بدون نوع از پیش تعریف شده را نمیتوان به عنوان یک عبارت سوئیچ استفاده کرد. نوع عبارت سوئیچ باید قابل مقایسه باشد.nil
به عبارت دیگر، با عبارت switch طوری رفتار میشود که گویی برای اعلان و مقداردهی اولیه یک متغیر موقت بدون نوع صریح استفاده شده است t؛ این همان مقداری است که برابری t هر عبارت case در مقابل آن x بررسی میشود.
در یک عبارت case یا default، آخرین دستور غیر تهی میتواند یک دستور "fallthrough" (احتمالاً با برچسب) باشد تا نشان دهد که کنترل باید از انتهای این عبارت به اولین دستور عبارت بعدی جریان یابد. در غیر این صورت، کنترل به انتهای دستور "switch" جریان مییابد. یک دستور "fallthrough" ممکن است به عنوان آخرین دستور از همه عبارات switch به جز آخرین دستور ظاهر شود.
عبارت سوئیچ میتواند با یک دستور ساده مقدم باشد که قبل از ارزیابی عبارت اجرا میشود.
پیشفرض: s3() حالتهای ۰، ۱، ۲، ۳: s1() موارد ۴، ۵، ۶، ۷: s2()
switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
switch x := f(); { // missing switch expression means "true"
case x < 0: return -x
default: return x
}
switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}در حالت x < 0: مقدار -x را برمیگرداند پیشفرض: مقدار x را برمیگرداند
حالت x < y: f1() محدودیت پیادهسازی: یک کامپایلر ممکن است اجازه ندهد که چندین عبارت case به یک ثابت یکسان ارزیابی شوند. برای مثال، کامپایلرهای فعلی اجازه نمیدهند که در عبارات case، ثابتهای عدد صحیح، ممیز شناور یا رشتهای تکراری وجود داشته باشند.
سوئیچ نوع، نوعها را به جای مقادیر مقایسه میکند. از جهات دیگر شبیه سوئیچ عبارت است. این سوئیچ با یک عبارت سوئیچ خاص مشخص میشود که به شکل یک اعلان نوع با استفاده از کلمه کلیدی type به جای یک نوع واقعی است:
switch x.(type) {
// cases
}سپس Case ها انواع واقعی را T با نوع پویای عبارت مطابقت میدهند x. همانند اعلانهای نوع، باید از نوع رابط x باشند، اما نه یک پارامتر نوع، و هر نوع غیر رابط ذکر شده در یک case باید نوع را پیادهسازی کند. انواع ذکر شده در case های یک سوئیچ نوع باید همگی متفاوت باشند.Tx
TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause = TypeSwitchCase ":" StatementList .
TypeSwitchCase = "case" TypeList | "default" .TypeSwitchGuard ممکن است شامل یک اعلان متغیر کوتاه باشد. وقتی از این شکل استفاده میشود، متغیر در انتهای TypeSwitchCase در بلوک ضمنی هر بند اعلان میشود. در بندهایی که یک case دقیقاً یک نوع را فهرست میکند، متغیر دارای آن نوع است؛ در غیر این صورت، متغیر دارای نوع عبارت در TypeSwitchGuard است.
به جای یک نوع، یک مورد میتواند از شناسه از پیش تعریف شده استفاده کند nil؛ آن مورد زمانی انتخاب میشود که عبارت موجود در TypeSwitchGuard یک nil مقدار رابط باشد. حداکثر یک nil مورد میتواند وجود داشته باشد.
عدد صحیح مورد: حالت بولی، رشته: پیش فرض: میتوانست بازنویسی شود:
switch i := x.(type) {
case nil:
printString("x is nil") // type of i is type of x (interface{})
case int:
printInt(i) // type of i is int
case float64:
printFloat64(i) // type of i is float64
case func(int) float64:
printFunction(i) // type of i is func(int) float64
case bool, string:
printString("type is bool or string") // type of i is type of x (interface{})
default:
printString("don't know the type") // type of i is type of x (interface{})
}یک پارامتر نوع یا یک نوع عمومی میتواند به عنوان یک نوع در یک مورد استفاده شود. اگر پس از نمونهسازی، آن نوع تکرار ورودی دیگری در سوئیچ شود، اولین مورد منطبق انتخاب میشود.
v := x // x is evaluated exactly once
if v == nil {
i := v // type of i is type of x (interface{})
printString("x is nil")
} else if i, isInt := v.(int); isInt {
printInt(i) // type of i is int
} else if i, isFloat64 := v.(float64); isFloat64 {
printFloat64(i) // type of i is float64
} else if i, isFunc := v.(func(int) float64); isFunc {
printFunction(i) // type of i is func(int) float64
} else {
_, isBool := v.(bool)
_, isString := v.(string)
if isBool || isString {
i := v // type of i is type of x (interface{})
printString("type is bool or string")
} else {
i := v // type of i is type of x (interface{})
printString("don't know the type")
}
}ممکن است قبل از محافظ سوئیچ نوع، یک دستور ساده قرار گیرد که قبل از ارزیابی محافظ اجرا میشود.
func f[P any](x any) int {
switch x.(type) {
case P:
return 0
case string:
return 1
case []P:
return 2
case []byte:
return 3
default:
return 4
}
}
var v1 = f[string]("foo") // v1 == 0
var v2 = f[byte]([]byte{}) // v2 == 2استفاده از دستور "fallthrough" در سوئیچ نوع مجاز نیست.
یک دستور "for" اجرای مکرر یک بلوک را مشخص میکند. سه شکل وجود دارد: تکرار ممکن است توسط یک شرط واحد، یک عبارت "for" یا یک عبارت "range" کنترل شود.
ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .در سادهترین شکل، یک دستور "for" اجرای مکرر یک بلوک را تا زمانی که یک شرط بولی به مقدار درست (true) ارزیابی شود، مشخص میکند. شرط قبل از هر تکرار ارزیابی میشود. اگر شرط وجود نداشته باشد، معادل مقدار بولی است true.
for a < b {
a *= 2
}
یک دستور "for" با شرط ForClause نیز توسط شرط آن کنترل میشود، اما علاوه بر این، میتواند یک دستور init و یک دستور post مانند یک انتساب، یک دستور افزایش یا کاهش را مشخص کند. دستور init میتواند یک اعلان متغیر کوتاه باشد، اما دستور post نباید این کار را انجام دهد.
ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .for i := 0; i < 10; i++ {
f(i)
}
اگر خالی نباشد، دستور init یک بار قبل از ارزیابی شرط برای اولین تکرار اجرا میشود؛ دستور post پس از هر بار اجرای بلوک (و فقط در صورتی که بلوک اجرا شده باشد) اجرا میشود. هر عنصر از ForClause میتواند خالی باشد اما سمیکالنها الزامی هستند، مگر اینکه فقط یک شرط وجود داشته باشد. اگر شرط وجود نداشته باشد، معادل مقدار بولی است true.
for cond { S() } is the same as for ; cond ; { S() }
for { S() } is the same as for true { S() }
هر تکرار، متغیر (یا متغیرهای) تعریفشدهی جداگانهی خود را دارد [Go 1.22]. متغیری که توسط اولین تکرار استفاده میشود، توسط دستور init تعریف میشود. متغیری که توسط هر تکرار بعدی استفاده میشود، قبل از اجرای دستور post به صورت ضمنی تعریف شده و در آن لحظه با مقدار متغیر تکرار قبلی مقداردهی اولیه میشود.
var prints []func()
for i := 0; i < 5; i++ {
prints = append(prints, func() { println(i) })
i++
}
for _, p := range prints {
p()
}چاپها
1
3
5
قبل از [Go 1.22]، تکرارها به جای داشتن متغیرهای جداگانهی خودشان، یک مجموعه از متغیرها را به اشتراک میگذاشتند. در این صورت، مثال بالا چاپ میشود
6
6
6
یک دستور "for" با یک بند "range" در تمام ورودیهای یک آرایه، slice، رشته یا map، مقادیر دریافتی در یک کانال، مقادیر صحیح از صفر تا یک حد بالا [Go 1.22]، یا مقادیر ارسالی به تابع yield یک تابع تکرارکننده [Go 1.23] تکرار میکند. برای هر ورودی، مقادیر تکرار را به متغیرهای تکرار مربوطه در صورت وجود اختصاص میدهد و سپس بلوک را اجرا میکند.
RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .عبارت سمت راست در عبارت "range"، عبارت range نامیده میشود که میتواند یک آرایه، اشارهگر به یک آرایه، slice، رشته، map، کانال مجاز برای عملیات دریافت، یک عدد صحیح یا یک تابع با امضای خاص باشد (به پایین مراجعه کنید). همانند یک انتساب، در صورت وجود، عملوندهای سمت چپ باید عبارات آدرسپذیر یا map شاخص باشند؛ آنها متغیرهای تکرار را نشان میدهند. اگر عبارت range یک تابع باشد، حداکثر تعداد متغیرهای تکرار به امضای تابع بستگی دارد. اگر عبارت range یک کانال یا عدد صحیح باشد، حداکثر یک متغیر تکرار مجاز است؛ در غیر این صورت ممکن است تا دو متغیر تکرار وجود داشته باشد. اگر آخرین متغیر تکرار، شناسه خالی باشد، عبارت range معادل همان عبارت بدون آن شناسه است.
عبارت محدوده x قبل از شروع حلقه ارزیابی میشود، با یک استثنا: اگر حداکثر یک متغیر تکرار وجود داشته باشد و x یا ثابت len(x) باشد، عبارت محدوده ارزیابی نمیشود.
فراخوانیهای تابع در سمت چپ، یک بار در هر تکرار ارزیابی میشوند. برای هر تکرار، در صورت وجود متغیرهای تکرار مربوطه، مقادیر تکرار به شرح زیر تولید میشوند:
Range expression 1st value 2nd value
array or slice a [n]E, *[n]E, or []E index i int a[i] E
string s string type index i int see below rune
map m map[K]V key k K m[k] V
channel c chan E, <-chan E element e E
integer value n integer type, or untyped int value i see below
function, 0 values f func(func() bool)
function, 1 value f func(func(V) bool) value v V
function, 2 values f func(func(K, V) bool) key k K v V
عبارت محدوده مقدار اول مقدار دوم
رشته s اندیس نوع رشته i زیر rune را نمیبینم مقدار صحیح n نوع صحیح، یا مقدار صحیح بدون نوع که در زیر میبینم برای یک آرایه، اشارهگر به آرایه یا مقدار slice a، مقادیر تکرار اندیس به ترتیب صعودی و با شروع از اندیس عنصر ۰ تولید میشوند. اگر حداکثر یک متغیر تکرار وجود داشته باشد، حلقهی محدوده مقادیر تکرار را از ۰ تا ۰ تولید میکند len(a)-1 و در آرایه یا خود slice اندیسگذاری نمیکند. برای یک nilslice، تعداد تکرارها ۰ است. برای یک مقدار رشتهای، عبارت "range" روی نقاط کد یونیکد در رشته تکرار میشود و از اندیس بایت 0 شروع میشود. در تکرارهای متوالی، مقدار اندیس، اندیس اولین بایت از نقاط کد رمزگذاری شده UTF-8 متوالی در رشته خواهد بود و مقدار دوم، از نوع rune، مقدار نقطه کد مربوطه خواهد بود. اگر تکرار با یک دنباله UTF-8 نامعتبر مواجه شود، مقدار دوم 0xFFFD، کاراکتر جایگزین یونیکد خواهد بود و تکرار بعدی یک بایت در رشته به جلو میرود. ترتیب تکرار روی mapها مشخص نشده است و تضمین نمیشود که از یک تکرار تا تکرار بعدی یکسان باشد. اگر یک ورودی map که هنوز به آن نرسیدهایم در طول تکرار حذف شود، مقدار تکرار مربوطه تولید نخواهد شد. اگر یک ورودی map در طول تکرار ایجاد شود، آن ورودی ممکن است در طول تکرار تولید شود یا از آن صرف نظر شود. انتخاب ممکن است برای هر ورودی ایجاد شده و از یک تکرار تا تکرار بعدی متفاوت باشد. اگر map باشد nil، تعداد تکرارها 0 است. برای کانالها، مقادیر تکرار تولید شده، مقادیر متوالی ارسال شده به کانال تا زمان بسته شدن کانال هستند. اگر کانال بسته باشد nil، عبارت محدوده برای همیشه مسدود میشود. برای یک مقدار صحیح n، که در آن nis از نوع صحیح یا یک ثابت صحیح بدون نوع است، مقادیر تکرار از 0 تا n-1 به ترتیب صعودی تولید میشوند. اگر nis از نوع صحیح باشد، مقادیر تکرار همان نوع را دارند. در غیر این صورت، نوع n طوری تعیین میشود که انگار به متغیر تکرار اختصاص داده شده است. به طور خاص: اگر متغیر تکرار از قبل موجود باشد، نوع مقادیر تکرار، نوع متغیر تکرار است که باید از نوع صحیح باشد. در غیر این صورت، اگر متغیر تکرار توسط عبارت "range" اعلام شده باشد یا وجود نداشته باشد، نوع مقادیر تکرار، نوع پیشفرض برای است n. اگر n<= 0 باشد، حلقه هیچ تکراری را اجرا نمیکند. برای یک تابع f، تکرار با فراخوانی f یک yield تابع جدید و سنتز شده به عنوان آرگومان ادامه مییابد. اگر yield قبل از تابع فراخوانی شود f، آرگومانها به yield مقادیر تکرار برای اجرای بدنه حلقه تبدیل میشوند. پس از هر تکرار متوالی حلقه، yield مقدار true را برمیگرداند و میتوان دوباره برای ادامه حلقه فراخوانی کرد. تا زمانی که بدنه حلقه خاتمه نیابد، بند "range" به تولید مقادیر تکرار به این روش برای هر yield فراخوانی تا f زمان بازگشت ادامه میدهد. اگر بدنه حلقه خاتمه یابد (مانند یک break دستور)، yield مقدار false را برمیگرداند و نباید دوباره فراخوانی شود.
اگر متغیرهای تکرار به طور صریح توسط عبارت "range" اعلام نشده باشند، باید از قبل موجود باشند. در این حالت، مقادیر تکرار مانند یک دستور انتساب به متغیرهای مربوطه اختصاص داده میشوند.
var testdata *struct {
a *[7]int
}
for i, _ := range testdata.a {
// testdata.a is never evaluated; len(testdata.a) is constant
// i ranges from 0 to 6
f(i)
}
var a [10]string
for i, s := range a {
// type of i is int
// type of s is string
// s == a[i]
g(i, s)
}
var key string
var val interface{} // element type of m is assignable to val
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for key, val = range m {
h(key, val)
}
// key == last map key encountered in iteration
// val == map[key]
var ch chan Work = producer()
for w := range ch {
doWork(w)
}
// empty a channel
for range ch {}
// call f(0), f(1), ... f(9)
for i := range 10 {
// type of i is int (default type for untyped constant 10)
f(i)
}
// invalid: 256 cannot be assigned to uint8
var u uint8
for u = range 256 {
}
// invalid: 1e3 is a floating-point constant
for range 1e3 {
}
// fibo generates the Fibonacci sequence
fibo := func(yield func(x int) bool) {
f0, f1 := 0, 1
for yield(f0) {
f0, f1 = f1, f0+f1
}
}
// print the Fibonacci numbers below 1000:
for x := range fibo {
if x >= 1000 {
break
}
fmt.Printf("%d ", x)
}
// output: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
// iteration support for a recursive tree data structure
type Tree[K cmp.Ordered, V any] struct {
left, right *Tree[K, V]
key K
value V
}
func (t *Tree[K, V]) walk(yield func(key K, val V) bool) bool {
return t == nil || t.left.walk(yield) && yield(t.key, t.value) && t.right.walk(yield)
}
func (t *Tree[K, V]) Walk(yield func(key K, val V) bool) {
t.walk(yield)
}
// walk tree t in-order
var t Tree[string, int]
for k, v := range t.Walk {
// process k, v
}رشته کلید متغیر
اگر نوع عبارت محدوده، یک پارامتر نوع باشد، همه انواع موجود در مجموعه نوع آن باید نوع زیربنایی یکسانی داشته باشند و عبارت محدوده باید برای آن نوع معتبر باشد، یا اگر مجموعه نوع شامل انواع کانال باشد، فقط باید شامل انواع کانال با انواع عناصر یکسان باشد و همه انواع کانال باید اجازه عملیات دریافت را بدهند.
یک دستور "go" اجرای یک فراخوانی تابع را به عنوان یک نخ کنترل همزمان مستقل یا goroutine در همان فضای آدرس آغاز میکند.
GoStmt = "go" Expression .عبارت باید یک فراخوانی تابع یا متد باشد؛ نمیتوان آن را داخل پرانتز قرار داد. فراخوانی توابع داخلی مانند دستورات عبارتی محدود شده است.
مقدار تابع و پارامترها طبق معمول در goroutine فراخوانیکننده ارزیابی میشوند، اما برخلاف فراخوانی معمولی، اجرای برنامه منتظر تکمیل تابع فراخوانیشده نمیماند. در عوض، تابع بهطور مستقل در یک goroutine جدید شروع به اجرا میکند. وقتی تابع خاتمه مییابد، goroutine آن نیز خاتمه مییابد. اگر تابع مقادیر بازگشتی داشته باشد، پس از اتمام تابع، آنها حذف میشوند.
go Server()
go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)برو به سرور ()
دستور "select" انتخاب میکند که کدام یک از مجموعهای از عملیات ارسال یا دریافت ممکن ادامه یابد. این دستور شبیه به دستور "switch" به نظر میرسد اما همه موارد آن به عملیات ارتباطی اشاره دارند.
SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase = "case" ( SendStmt | RecvStmt ) | "default" .
RecvStmt = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
RecvExpr = Expression .یک مورد با RecvStmt میتواند نتیجه یک RecvExpr را به یک یا دو متغیر اختصاص دهد، که میتوانند با استفاده از یک اعلان متغیر کوتاه اعلام شوند. RecvExpr باید یک عملیات دریافت (احتمالاً داخل پرانتز) باشد. حداکثر یک مورد پیشفرض میتواند وجود داشته باشد و میتواند در هر جایی از لیست موارد ظاهر شود.
اجرای یک دستور "select" در چند مرحله انجام میشود:
برای تمام موارد موجود در دستور، عملوندهای کانال عملیات دریافت و عبارات کانال و سمت راست دستورات ارسال دقیقاً یک بار، به ترتیب منبع، پس از وارد کردن دستور "select"، ارزیابی میشوند. نتیجه مجموعهای از کانالها برای دریافت از یا ارسال به و مقادیر مربوطه برای ارسال است. هرگونه عوارض جانبی در آن ارزیابی، صرف نظر از اینکه کدام عملیات ارتباطی (در صورت وجود) برای ادامه انتخاب شده باشد، رخ خواهد داد. عبارات سمت چپ RecvStmt با اعلان یا انتساب متغیر کوتاه هنوز ارزیابی نشدهاند. اگر یک یا چند ارتباط بتوانند ادامه پیدا کنند، یک ارتباط که میتواند ادامه پیدا کند از طریق یک انتخاب شبهتصادفی یکنواخت انتخاب میشود. در غیر این صورت، اگر حالت پیشفرض وجود داشته باشد، آن حالت انتخاب میشود. اگر حالت پیشفرضی وجود نداشته باشد، دستور "select" تا زمانی که حداقل یکی از ارتباطات بتواند ادامه پیدا کند، مسدود میشود. مگر اینکه مورد انتخاب شده، مورد پیشفرض باشد، عملیات ارتباطی مربوطه اجرا میشود. اگر مورد انتخاب شده یک RecvStmt با اعلان متغیر کوتاه یا انتساب باشد، عبارات سمت چپ ارزیابی شده و مقدار (یا مقادیر) دریافتی انتساب داده میشوند. لیست دستورات مربوط به کیس انتخاب شده اجرا میشود. از آنجایی که ارتباط در nil کانالها هرگز نمیتواند ادامه یابد، انتخابی که فقط nil کانالها را در بر میگیرد و هیچ مورد پیشفرضی ندارد، برای همیشه مسدود میشود.
پیش فرض:
var a []int
var c, c1, c2, c3, c4 chan int
var i1, i2 int
select {
case i1 = <-c1:
print("received ", i1, " from c1\n")
case c2 <- i2:
print("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
print("received ", i3, " from c3\n")
} else {
print("c3 is closed\n")
}
case a[f()] = <-c4:
// same as:
// case t := <-c4
// a[f()] = t
default:
print("no communication\n")
}
for { // send random sequence of bits to c
select {
case c <- 0: // note: no statement, no fallthrough, no folding of cases
case c <- 1:
}
}
select {} // block foreverیک دستور "return" در یک تابع، F اجرای را خاتمه میدهد F و به صورت اختیاری یک یا چند مقدار نتیجه را ارائه میدهد. هر تابعی که توسط به تعویق افتاده باشد، قبل از اینکه به فراخوانندهاش بازگردد، F اجرا میشود.F
ReturnStmt = "return" [ ExpressionList ] .در تابعی که نوع نتیجه ندارد، دستور "return" نباید هیچ مقدار نتیجهای را مشخص کند.
func noResult() {
return
}سه راه برای بازگرداندن مقادیر از یک تابع با نوع نتیجه وجود دارد:
مقدار یا مقادیر برگشتی میتوانند به طور صریح در دستور "return" فهرست شوند. هر عبارت باید تک مقداری باشد و قابل انتساب به عنصر مربوطه از نوع نتیجه تابع باشد.
لیست عبارات در دستور "return" میتواند یک فراخوانی واحد به یک تابع چند مقداری باشد. نتیجه مانند این است که هر مقدار برگردانده شده از آن تابع به یک متغیر موقت با نوع مقدار مربوطه اختصاص داده شده و به دنبال آن یک دستور "return" این متغیرها را فهرست میکند، که در آن نقطه قوانین حالت قبلی اعمال میشود. اگر نوع نتیجه تابع، نامهایی را برای پارامترهای نتیجه آن مشخص کند، لیست عبارت میتواند خالی باشد. پارامترهای نتیجه به عنوان متغیرهای محلی معمولی عمل میکنند و تابع میتواند در صورت لزوم به آنها مقادیری اختصاص دهد. عبارت "return" مقادیر این متغیرها را برمیگرداند.
صرف نظر از نحوه اعلان آنها، تمام مقادیر نتیجه هنگام ورود به تابع، با مقادیر صفر برای نوع خود مقداردهی اولیه میشوند. یک عبارت "return" که results را مشخص میکند، پارامترهای نتیجه را قبل از اجرای هرگونه تابع به تعویق افتاده تنظیم میکند.
func f(n int) (res int, err error) {
if _, err := f(n-1); err != nil {
return // invalid return statement: err is shadowed
}
return
}محدودیت پیادهسازی: اگر یک موجودیت متفاوت (ثابت، نوع یا متغیر) با نام مشابه پارامتر نتیجه در محدودهی محل return وجود داشته باشد، کامپایلر ممکن است اجازهی استفاده از لیست عبارات خالی را در دستور "return" ندهد.
دستور "break" اجرای داخلیترین دستور "for"، "switch" یا "select" را در همان تابع خاتمه میدهد.
BreakStmt = "break" [ Label ] .اگر برچسبی وجود داشته باشد، باید برچسب یک دستور "for"، "switch" یا "select" باشد و همان برچسبی است که اجرای آن خاتمه مییابد.
OuterLoop:
for i = 0; i < n; i++ {
for j = 0; j < m; j++ {
switch a[i][j] {
case nil:
state = Error
break OuterLoop
case item:
state = Found
break OuterLoop
}
}
}حلقه بیرونی:
یک دستور "continue" با پیش بردن کنترل به انتهای بلوک حلقه، تکرار بعدی درونیترین حلقهی "for" را آغاز میکند. حلقهی "for" باید درون همان تابع باشد.
ContinueStmt = "continue" [ Label ] .اگر برچسبی وجود داشته باشد، باید برچسب یک دستور "for" باشد و این همان برچسبی است که اجرای آن پیشرفت میکند.
RowLoop:
for y, row := range rows {
for x, data := range row {
if data == endOfRow {
continue RowLoop
}
row[x] = data + bias(x, y)
}
}حلقه ردیف:
یک دستور "goto" کنترل را به دستوری با برچسب مربوطه در همان تابع منتقل میکند.
GotoStmt = "goto" Label .goto Error
خطا در رفتن به اجرای دستور "goto" نباید باعث شود متغیرهایی که در نقطه goto در محدوده نبودند، وارد محدوده شوند. برای مثال، این مثال:
goto L // BAD
v := 3
L:
ل: اشتباه است زیرا پرش به برچسب، L ایجاد را نادیده میگیرد v.
یک دستور "goto" خارج از یک بلوک نمیتواند به برچسبی درون آن بلوک پرش کند. برای مثال، این مثال:
if n%2 == 1 {
goto L1
}
for n > 0 {
f()
n--
L1:
f()
n--
}
اشتباه است زیرا برچسب L1 درون بلوک دستور "for" است اما خود " gotois" نیست.
یک دستور "fallthrough" کنترل را به اولین دستور از بند case بعدی در یک دستور "switch" منتقل میکند. این دستور فقط میتواند به عنوان آخرین دستور غیر تهی در چنین بندی استفاده شود.
FallthroughStmt = "fallthrough" .یک دستور "defer" تابعی را فراخوانی میکند که اجرای آن تا لحظه بازگشت تابع اطراف به تعویق میافتد، یا به این دلیل که تابع اطراف یک دستور return را اجرا کرده، به انتهای بدنه تابع خود رسیده، یا به این دلیل که goroutine مربوطه در حال panic شدن است.
DeferStmt = "defer" Expression .عبارت باید یک فراخوانی تابع یا متد باشد؛ نمیتوان آن را داخل پرانتز قرار داد. فراخوانی توابع داخلی مانند دستورات عبارتی محدود شده است.
هر بار که یک دستور "defer" اجرا میشود، مقدار تابع و پارامترهای فراخوانی طبق معمول ارزیابی شده و دوباره ذخیره میشوند، اما تابع اصلی فراخوانی نمیشود. در عوض، توابع defer شده بلافاصله قبل از بازگشت تابع اطراف، به ترتیب معکوسی که به تعویق افتاده بودند، فراخوانی میشوند. یعنی، اگر تابع اطراف از طریق یک دستور return صریح بازگشت دهد، توابع defer شده پس از تنظیم پارامترهای نتیجه توسط آن دستور return اما قبل از بازگشت تابع به فراخوانندهاش اجرا میشوند. اگر مقدار یک تابع defer شده برابر با باشد، هنگام فراخوانی تابع، nil اجرا دچار اختلال میشود، نه هنگام اجرای دستور "defer".
برای مثال، اگر تابع به تعویق افتاده یک تابع تحتاللفظی باشد و تابع اطراف آن پارامترهای نتیجه نامگذاری شدهای داشته باشد که در محدوده آن تابع تحتاللفظی قرار دارند، تابع به تعویق افتاده ممکن است قبل از بازگشت پارامترهای نتیجه به آنها دسترسی پیدا کرده و آنها را تغییر دهد. اگر تابع به تعویق افتاده مقادیر بازگشتی داشته باشد، پس از اتمام تابع، آنها حذف میشوند. (همچنین به بخش مدیریت panicها مراجعه کنید.)
lock(l)
defer unlock(l) // unlocking happens before surrounding function returns
// prints 3 2 1 0 before surrounding function returns
for i := 0; i <= 3; i++ {
defer fmt.Print(i)
}
// f returns 42
func f() (result int) {
defer func() {
// result is accessed after it was set to 6 by the return statement
result *= 7
}()
return 6
}قفل (l)
توابع داخلی از پیش تعریف شدهاند. آنها مانند هر تابع دیگری فراخوانی میشوند، اما برخی از آنها به جای یک عبارت، یک نوع را به عنوان آرگومان اول میپذیرند.
توابع داخلی (built-in functions) انواع داده استاندارد Go را ندارند، بنابراین فقط میتوانند در عبارات فراخوانی (call expressions) ظاهر شوند؛ نمیتوان از آنها به عنوان مقادیر تابع استفاده کرد.
توابع داخلی append و copy در عملیات slice مشترک کمک میکنند. برای هر دو تابع، نتیجه مستقل از این است که آیا حافظهای که آرگومانها به آن ارجاع میدهند، همپوشانی دارد یا خیر.
تابع متغیر append صفر یا چند مقدار را x به یک slice s از نوع اضافه میکند S و slice حاصل را که آن هم از نوع است، برمیگرداند. S مقادیر x به پارامتری از نوع ارسال میشوند...E که در آن E نوع عنصر است S و قوانین مربوط به ارسال پارامتر اعمال میشود. به عنوان یک مورد خاص، appendslice ی را نیز میپذیرد که نوع آن قابل انتساب به نوع است []byte و آرگومان دوم آن از string نوع و به دنبال آن است.... این فرم بایتهای رشته را اضافه میکند.
append(s S, x ...E) S // E is the element type of S
اگر ظرفیت s به اندازه کافی بزرگ نباشد که مقادیر اضافی را در خود جای دهد، یک آرایه زیربنایی جدید و به اندازه کافی بزرگ append اختصاص میدهد که هم عناصر slice موجود و هم مقادیر اضافی را در خود جای میدهد. در غیر این صورت، append از آرایه زیربنایی دوباره استفاده میکند.
این تابع copy عناصر slice را از یک منبع src به یک مقصد کپی میکند dst و تعداد عناصر کپی شده را برمیگرداند. هر دو آرگومان باید نوع عنصر یکسانی E داشته باشند و باید قابل انتساب به یک slice از نوع باشند []E. تعداد عناصر کپی شده حداقل len(src) و است len(dst). به عنوان یک مورد خاص، copy یک آرگومان مقصد را نیز میپذیرد که قابل انتساب به نوع []byte با آرگومان منبع از یک string نوع است. این فرم بایتها را از رشته به slice بایت کپی میکند.
s0 := []int{0, 0}
s1 := append(s0, 2) // append a single element s1 is []int{0, 0, 2}
s2 := append(s1, 3, 5, 7) // append multiple elements s2 is []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...) // append a slice s3 is []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 is []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
var t []interface{}
t = append(t, 42, 3.1415, "foo") // t is []interface{}{42, 3.1415, "foo"}
var b []byte
b = append(b, "bar"...) // append string contents b is []byte{'b', 'a', 'r' }مثالها:
copy(dst, src []T) int
copy(dst []byte, src string) int
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:]) // n1 == 6, s is []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:]) // n2 == 4, s is []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!") // n3 == 5, b is []byte("Hello")پارامتر نوع clear(t) را در زیر ببینید اگر نوع آرگومان ورودی، clear یک پارامتر نوع باشد، تمام انواع موجود در مجموعه نوع آن باید map یا slice باشند و clear عملیاتی را انجام میدهد که مربوط به آرگومان نوع واقعی است.
Call Argument type Result
clear(m) map[K]T deletes all entries, resulting in an
empty map (len(m) == 0)
clear(s) []T sets all elements up to the length of
s to the zero value of T
clear(t) type parameter see below
برای یک کانال ch، تابع داخلی close(ch) ثبت میکند که دیگر هیچ مقداری به کانال ارسال نخواهد شد. اگر ch کانال فقط دریافت باشد، خطا است. ارسال یا بستن یک کانال بسته باعث ایجاد اختلال در زمان اجرا میشود. بستن کانال تهی نیز باعث ایجاد اختلال در زمان اجرا میشود. پس از فراخوانی close و پس از دریافت هرگونه مقدار ارسال شده قبلی، عملیات دریافت، مقدار صفر را برای نوع کانال بدون مسدود شدن برمیگرداند. عملیات دریافت چند مقداری، یک مقدار دریافتی را به همراه نشانهای از بسته بودن کانال برمیگرداند.
اگر نوع آرگومان ورودی close یک پارامتر نوع باشد، تمام انواع موجود در مجموعه نوع آن باید کانال باشند. اگر هر یک از این کانالها فقط گیرنده باشند، خطا رخ میدهد.
سه تابع، اعداد مختلط را به هم متصل و از هم جدا میکنند. تابع داخلی، complex یک مقدار مختلط را از یک بخش حقیقی و موهومی اعشاری میسازد، در حالی که real بخشهای imag حقیقی و موهومی یک مقدار مختلط را استخراج میکند.
complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT
مختلط (قطعه حقیقی، قطعه خیالی، قطعه شناور) مختلط حقیقی (مختلط) شناور تصویر (پیچیده) floatT نوع آرگومانها و مقدار برگشتی با هم مطابقت دارند. برای complex، دو آرگومان باید از یک نوع ممیز شناور باشند و نوع برگشتی، نوع مختلط با اجزای ممیز شناور مربوطه است: complex64 برای float32 آرگومانها، و complex128 برای float64 آرگومانها. اگر یکی از آرگومانها به یک ثابت بدون نوع ارزیابی شود، ابتدا به طور ضمنی به نوع آرگومان دیگر تبدیل میشود. اگر هر دو آرگومان به ثابتهای بدون نوع ارزیابی شوند، باید اعداد غیر مختلط باشند یا بخشهای موهومی آنها باید صفر باشد و مقدار برگشتی تابع یک ثابت مختلط بدون نوع است.
برای real و imag، آرگومان باید از نوع مختلط باشد و نوع برگشتی، نوع اعشاری مربوطه است: float32 برای یک complex64 آرگومان، و float64 برای یک complex128 آرگومان. اگر آرگومان به یک ثابت بدون نوع ارزیابی شود، باید یک عدد باشد و مقدار برگشتی تابع، یک ثابت بدون نوع اعشاری است.
آرگومانهایی از نوع پارامتر مجاز نیستند.
var a = complex(2, -2) // complex128
const b = complex(1.0, -1.4) // untyped complex constant 1 - 1.4i
x := float32(math.Cos(math.Pi/2)) // float32
var c64 = complex(5, -x) // complex64
var s int = complex(1, 0) // untyped complex constant 1 + 0i can be converted to int
_ = complex(1, 2<<s) // illegal: 2 assumes floating-point type, cannot shift
var rl = real(c64) // float32
var im = imag(a) // float64
const c = imag(b) // untyped constant -1.4
_ = imag(3 << s) // illegal: 3 assumes complex type, cannot shiftتابع داخلی، delete عنصر دارای کلید را k از یک map m حذف میکند. مقدار k باید قابل انتساب به نوع کلید باشد m.
delete(m, k) // remove element m[k] from map m
اگر نوع m یک پارامتر نوع باشد، همه نوعهای موجود در آن مجموعه نوع باید map باشند و همه آنها باید انواع کلید یکسانی داشته باشند.
توابع داخلی len و cap آرگومانهایی از انواع مختلف را دریافت میکنند و نتیجهای از نوع... را برمیگردانند int. پیادهسازی تضمین میکند که نتیجه همیشه در یک... قرار میگیرد int.
Call Argument type Result
len(s) string type string length in bytes
[n]T, *[n]T array length (== n)
[]T slice length
map[K]T map length (number of defined keys)
chan T number of elements queued in channel buffer
type parameter see below
cap(s) [n]T, *[n]T array length (== n)
[]T slice capacity
chan T channel buffer capacity
type parameter see below
اگر نوع آرگومان یک پارامتر نوع P باشد، فراخوانی len(e)(یا cap(e) به ترتیب) باید برای هر نوع در P مجموعه نوع معتبر باشد. نتیجه، طول (یا به ترتیب ظرفیت) آرگومانی است که نوع آن با آرگومان نوعی که با آن نمونهسازی شده P است، مطابقت دارد.
ظرفیت یک slice، تعداد عناصری است که در آرایه زیرین به آنها فضا اختصاص داده شده است. در هر زمان رابطه زیر برقرار است:
0 <= len(s) <= cap(s)
طول یک nilslice، map یا کانال صفر است. ظرفیت یک nilslice یا کانال صفر است.
عبارت اگر یک ثابت رشتهای باشد، ثابت len(s) است. عبارات و اگر نوع آن یک آرایه یا اشارهگر به یک آرایه باشد و عبارت شامل دریافتهای کانال یا فراخوانیهای تابع (غیرثابت) نباشد، ثابت هستند؛ در این حالت ارزیابی نمیشود. در غیر این صورت، فراخوانیهای و ثابت نیستند و ارزیابی میشوند.slen(s)cap(s)ssslencaps
const (
c1 = imag(2i) // imag(2i) = 2.0 is a constant
c2 = len([10]float64{2}) // [10]float64{2} contains no function calls
c3 = len([10]float64{c1}) // [10]float64{c1} contains no function calls
c4 = len([10]float64{imag(2i)}) // imag(2i) is a constant and no function call is issued
c5 = len([10]float64{imag(z)}) // invalid: imag(z) is a (non-constant) function call
)
var z complex128وار زد کمپلکس128
تابع داخلی make یک نوع میگیرد T که باید از نوع slice، map یا channel یا یک پارامتر type باشد و به صورت اختیاری با لیستی از عبارات مختص به نوع دنبال میشود. این تابع مقداری از نوع T(نه *T) را برمیگرداند. حافظه همانطور که در بخش مقادیر اولیه توضیح داده شده است، مقداردهی اولیه میشود.
Call Type T Result
make(T, n) slice slice of type T with length n and capacity n
make(T, n, m) slice slice of type T with length n and capacity m
make(T) map map of type T
make(T, n) map map of type T with initial space for approximately n elements
make(T) channel unbuffered channel of type T
make(T, n) channel buffered channel of type T, buffer size n
make(T, n) type parameter see below
make(T, n, m) type parameter see below
نتیجه فراخوانی نوع T
make(T, n) slice ی از نوع T با طول n و ظرفیت n make(T, n, m) slice ی از نوع T با طول n و ظرفیت m
s := make([]int, 10, 100) // slice with len(s) == 10, cap(s) == 100
s := make([]int, 1e3) // slice with len(s) == cap(s) == 1000
s := make([]int, 1<<63) // illegal: len(s) is not representable by a value of type int
s := make([]int, 10, 0) // illegal: len(s) > cap(s)
c := make(chan int, 10) // channel with a buffer size of 10
m := make(map[string]int, 100) // map with initial space for approximately 100 elementsmake(T, n) map map ی از نوع T با فضای اولیه برای تقریباً n عنصر
make(T, n) کانال بافر شده از نوع T، اندازه بافر n
پارامتر نوع make(T, n) را در زیر ببینید پارامتر نوع make(T, n, m) را در زیر ببینید اگر آرگومان اول یک پارامتر نوع باشد، همه انواع موجود در مجموعه نوع آن باید نوع زیربنایی یکسانی داشته باشند که باید یک نوع slice یا map باشد، یا اگر انواع کانال وجود دارد، فقط باید انواع کانال وجود داشته باشد، همه آنها باید نوع عنصر یکسانی داشته باشند و جهتهای کانال نباید با هم تداخل داشته باشند.
هر یک از آرگومانهای size n و m باید از نوع عدد صحیح باشند، مجموعهای از انواع داشته باشند که فقط شامل انواع عدد صحیح باشد، یا یک ثابت بدون نوع باشند. یک آرگومان ثابت با اندازه ثابت باید غیر منفی و قابل نمایش با مقداری از نوع باشد int؛ اگر یک ثابت بدون نوع باشد، به آن نوع داده میشود int. اگر هر دو n و m ارائه شوند و ثابت باشند، n نباید بزرگتر از باشند m. برای sliceها و کانالها، اگر n منفی یا بزرگتر از m مقدار زمان اجرا باشد، یک خطای زمان اجرا رخ میدهد.
فراخوانی make با اشاره به نوع و اندازهی map، n یک map با فضای اولیه برای نگهداری عناصر map ایجاد میکند n. رفتار دقیق آن وابسته به پیادهسازی است.
توابع داخلی min و max به ترتیب کوچکترین یا بزرگترین مقدار تعداد ثابتی از آرگومانها از انواع مرتب را محاسبه میکنند. حداقل باید یک آرگومان وجود داشته باشد [Go 1.21].
همان قوانین نوع دادهای که برای عملگرها اعمال میشود: برای آرگومانهای مرتبشده x و y، min(x, y) معتبر است اگر x + y معتبر باشد، و نوع آن min(x, y) از نوع باشد x + y(و به طور مشابه برای max). اگر همه آرگومانها ثابت باشند، نتیجه ثابت است.
var x, y int
m := min(x) // m == x
m := min(x, y) // m is the smaller of x and y
m := max(x, y, 10) // m is the larger of x and y but at least 10
c := max(1, 2.0, 10) // c == 10.0 (floating-point kind)
f := max(0, float32(x)) // type of f is float32
var s []string
_ = min(s...) // invalid: slice arguments are not permitted
t := max("", "foo", "bar") // t == "foo" (string kind)برای آرگومانهای عددی، با فرض اینکه همه NaN ها برابر هستند min و max خاصیت جابجایی و شرکتپذیری دارند:
min(x, y) == min(y, x)
min(x, y, z) == min(min(x, y), z) == min(x, min(y, z))
xy حداقل (x، y) حداکثر (x، y)
x y min(x, y) max(x, y)
-0.0 0.0 -0.0 0.0 // negative zero is smaller than (non-negative) zero
-Inf y -Inf y // negative infinity is smaller than any other number
+Inf y y +Inf // positive infinity is larger than any other number
NaN y NaN NaN // if any argument is a NaN, the result is a NaN
برای آرگومانهای رشتهای، نتیجه for min اولین آرگومانی است که کوچکترین (یا max بزرگترین) مقدار را دارد، که به صورت بایت به بایت مقایسه میشود:
min(x, y) == if x <= y then x else y
min(x, y, z) == min(min(x, y), z)
تابع داخلی یک متغیر new جدید و مقداردهی اولیه شده ایجاد میکند و یک اشارهگر به آن برمیگرداند. این تابع یک آرگومان واحد میپذیرد که میتواند یک نوع یا یک عبارت باشد.
اگر آرگومان از نوع باشد T، آنگاه new(T) متغیری از نوع را که مقدار T اولیه آن صفر است، به آن اختصاص میدهد.
اگر آرگومان یک عبارت باشد x، آنگاه new(x) یک متغیر از نوع را که x مقدار اولیه آن است، به مقدار اختصاص میدهد x. اگر آن مقدار یک ثابت بدون نوع باشد، ابتدا به طور ضمنی به نوع پیشفرض خود تبدیل میشود؛ اگر یک مقدار بولی بدون نوع باشد، ابتدا به طور ضمنی به نوع bool تبدیل میشود. شناسه از پیش تعریف شده را نمیتوان به عنوان آرگومان برای استفاده کرد.nilnew
برای مثال، new(int) و new(123) هر کدام یک اشارهگر به یک متغیر جدید از نوع برمیگردانند int. مقدار متغیر اول 0 و مقدار متغیر دوم است 123. به طور مشابه
type S struct { a int; b float64 }
new(S)جدید (S)
دو تابع داخلی، panic و recover، در گزارشدهی و مدیریت خطاهای زمان اجرا و شرایط خطای تعریفشده توسط برنامه کمک میکنند.
func panic(interface{})
func recover() interface{}
هنگام اجرای یک تابع F، یک فراخوانی صریح به panic یا یک panic زمان اجرا، اجرای را خاتمه میدهد. سپس F هر تابعی که توسط به تعویق افتاده باشد، طبق معمول اجرا میشود. در مرحله بعد، هر تابع به تعویق افتادهای که توسط caller اجرا میشود، اجرا میشود و به همین ترتیب تا هر تابع سطح بالا در goroutine که به تعویق افتاده باشد، ادامه مییابد. در آن نقطه، برنامه خاتمه مییابد و شرط خطا، از جمله مقدار آرگومان to، گزارش میشود. این توالی خاتمه، panicking نامیده میشود.FFpanic
panic(42)
panic("unreachable")
panic(Error("cannot parse"))
panic (42) panic ("دسترسی ناپذیر") panic(خطا("نمیتوان تجزیه کرد")) این recover تابع به یک برنامه اجازه میدهد تا رفتار یک goroutine در حال panic را مدیریت کند. فرض کنید یک تابع، G تابعی را D که فراخوانی میکند به تعویق میاندازد recover و یک panic در تابعی در همان goroutine که G در حال اجرا است، رخ میدهد. هنگامی که اجرای توابع به تعویق افتاده به میرسد D، مقدار برگشتی D فراخوانی to recover برابر با مقداری خواهد بود که به فراخوانی منتقل میشود panic. اگر D به طور معمول و بدون شروع یک جدید برگردد panic، توالی panic متوقف میشود. در این حالت، وضعیت توابعی که بین G و فراخوانی to فراخوانی شدهاند panic، نادیده گرفته میشود و اجرای عادی از سر گرفته میشود. سپس هر تابعی که G قبل از آن به تعویق افتاده D باشد، اجرا میشود و G اجرای با بازگشت به فراخوانی کننده آن خاتمه مییابد.
مقدار برگشتی of زمانی recover است nil که goroutine در حال panicing نباشد یا recover مستقیماً توسط یک تابع deferred فراخوانی نشده باشد. برعکس، اگر یک goroutine در حال panicing باشد و recover مستقیماً توسط یک تابع deferred فراخوانی شده باشد، مقدار برگشتی of recover تضمین میشود که اینطور نباشد nil. برای اطمینان از این امر، فراخوانی panic با یک nil مقدار رابط (یا یک مقدار untyped nil) باعث panic زمان اجرا میشود.
تابع protect در مثال زیر، آرگومان تابع را فراخوانی میکند g و از فراخوانیکنندگان در برابر اختلالات زمان اجرا ناشی از محافظت میکند g.
func protect(g func()) {
defer func() {
log.Println("done") // Println executes normally even if there is a panic
if x := recover(); x != nil {
log.Printf("run time panic: %v", x)
}
}()
log.Println("start")
g()
}پیادهسازیهای فعلی چندین تابع داخلی مفید در طول بوتاسترپ ارائه میدهند. این توابع برای کامل بودن مستند شدهاند، اما تضمینی برای ماندن آنها در زبان وجود ندارد. آنها نتیجهای را برنمیگردانند.
Function Behavior
print prints all arguments; formatting of arguments is implementation-specific
println like print but prints spaces between arguments and a newline at the end
رفتار تابع
دستور print تمام آرگومانها را چاپ میکند؛ قالببندی آرگومانها وابسته به پیادهسازی است. println مانند print عمل میکند اما بین آرگومانها فاصله و در انتها یک خط جدید چاپ میکند. محدودیت پیادهسازی: print و نیازی به پذیرش انواع آرگومان دلخواه ندارد، اما چاپ انواع println بولی، عددی و رشتهای باید پشتیبانی شود.
برنامههای Go با پیوند دادن بستهها به یکدیگر ساخته میشوند. یک بسته به نوبه خود از یک یا چند فایل منبع ساخته میشود که با هم ثابتها، انواع، متغیرها و توابع متعلق به بسته را تعریف میکنند و در همه فایلهای همان بسته قابل دسترسی هستند. این عناصر را میتوان در بسته دیگری صادر و استفاده کرد.
هر فایل منبع شامل یک عبارت package است که package مربوط به آن را تعریف میکند، و به دنبال آن مجموعهای احتمالاً خالی از اعلانهای import میآید که package هایی را که مایل به استفاده از محتویات آنها است، اعلام میکنند، و به دنبال آن مجموعهای احتمالاً خالی از اعلانهای توابع، نوعها، متغیرها و ثابتها میآید.
SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .یک عبارت package هر فایل منبع را آغاز میکند و بستهای را که فایل به آن تعلق دارد، تعریف میکند.
PackageClause = "package" PackageName .
PackageName = identifier .PackageName نباید شناسه خالی باشد.
package mathریاضی بسته مجموعهای از فایلها که نام بستهی یکسانی دارند، پیادهسازی یک بسته را تشکیل میدهند. یک پیادهسازی ممکن است ایجاب کند که تمام فایلهای منبع یک بسته در یک دایرکتوری قرار داشته باشند.
یک اعلان import بیان میکند که فایل منبع حاوی اعلان به عملکرد بستهی وارد شده (§ مقداردهی اولیه و اجرای برنامه) بستگی دارد و دسترسی به شناسههای صادر شدهی آن بسته را ممکن میسازد. import یک شناسه (PackageName) را برای استفاده جهت دسترسی و یک ImportPath را که بستهی وارد شده را مشخص میکند، نامگذاری میکند.
ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .PackageName در شناسههای واجد شرایط برای دسترسی به شناسههای صادر شده از بسته در فایل منبع وارد کننده استفاده میشود. این شناسه در بلوک فایل اعلام میشود. اگر PackageName حذف شود، به طور پیشفرض شناسه مشخص شده در بند package بسته وارد شده در نظر گرفته میشود. اگر به جای نام، یک نقطه (.) به صورت صریح ظاهر شود، تمام شناسههای صادر شده بسته که در بلوک بسته آن بسته اعلام شدهاند، در بلوک فایل فایل منبع وارد کننده اعلام میشوند و باید بدون یک توصیف کننده به آنها دسترسی پیدا کرد.
تفسیر ImportPath وابسته به پیادهسازی است، اما معمولاً زیررشتهای از نام کامل فایل بسته کامپایلشده است و ممکن است به مخزنی از بستههای نصبشده مرتبط باشد.
یک بسته کامپایل شده حاوی عبارت package را در نظر بگیرید package math که تابع export را صادر میکند Sin و بسته کامپایل شده را در فایلی که با مشخص شده است نصب میکند. این جدول نحوه دسترسی به آن را در فایلهایی که بسته را import میکنند، پس از انواع مختلف اعلان import "lib/math"نشان میدهد.Sin
اظهارنامه واردات نام محلی سین
Import declaration Local name of Sin
import "lib/math" math.Sin
import m "lib/math" m.Sin
import . "lib/math" Sin
وارد کردن "lib/math" با math.Sin دستور import m "lib/math" با استفاده از m.Sin یک اعلان واردات، یک رابطه وابستگی بین بسته واردکننده و بسته وارد شده را اعلام میکند. واردات مستقیم یا غیرمستقیم یک بسته یا واردات مستقیم یک بسته بدون اشاره به هیچ یک از شناسههای صادر شده آن، غیرقانونی است. برای واردات یک بسته صرفاً برای عوارض جانبی آن (مقداردهی اولیه)، از شناسه خالی به عنوان نام صریح بسته استفاده کنید:
import _ "lib/math"
وارد کردن _ "lib/math"
در اینجا یک بسته کامل Go وجود دارد که یک غربالگر اصلی همزمان را پیادهسازی میکند.
package main
import "fmt"
// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan<- int) {
for i := 2; ; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}
// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
for i := range src { // Loop over values received from 'src'.
if i%prime != 0 {
dst <- i // Send 'i' to channel 'dst'.
}
}
}
// The prime sieve: Daisy-chain filter processes together.
func sieve() {
ch := make(chan int) // Create a new channel.
go generate(ch) // Start generate() as a subprocess.
for {
prime := <-ch
fmt.Print(prime, "\n")
ch1 := make(chan int)
go filter(ch, ch1, prime)
ch = ch1
}
}
func main() {
sieve()
}وارد کردن "fmt"
وقتی برای یک متغیر، چه از طریق اعلان یا فراخوانی new، یا وقتی یک مقدار جدید، چه از طریق یک لیترال مرکب یا فراخوانی make، ایجاد میشود و هیچ مقداردهی اولیه صریحی ارائه نمیشود، به متغیر یا مقدار، یک مقدار پیشفرض داده میشود. هر عنصر از چنین متغیر یا مقداری برای نوع خود روی مقدار صفر false تنظیم میشود: برای مقادیر بولی، 0 برای انواع عددی، ""برای رشتهها و nil برای اشارهگرها، توابع، رابطها، sliceها، کانالها و mapها. این مقداردهی اولیه به صورت بازگشتی انجام میشود، بنابراین برای مثال، هر عنصر از یک آرایه از ساختارها، در صورت عدم تعیین مقدار، فیلدهای خود را صفر خواهد کرد.
این دو اعلان ساده معادل هستند:
var i int
var i int = 0بعد از
type T struct { i int; f float64; next *T }
t := new(T)موارد زیر برقرار است:
t.i == 0
t.f == 0.0
t.next == nil
همین امر پس از آن نیز صادق خواهد بود
var t Tدر یک بسته، مقداردهی اولیه متغیر در سطح بسته به صورت مرحلهای انجام میشود، که در هر مرحله، متغیری که از نظر ترتیب اعلان، زودتر از بقیه قرار دارد و هیچ وابستگی به متغیرهای مقداردهی نشده ندارد، انتخاب میشود.
به طور دقیقتر، یک متغیر سطح بسته در صورتی آماده برای مقداردهی اولیه در نظر گرفته میشود که هنوز مقداردهی اولیه نشده باشد و یا هیچ عبارت مقداردهی اولیهای نداشته باشد یا عبارت مقداردهی اولیه آن هیچ وابستگی به متغیرهای مقداردهی نشده نداشته باشد. مقداردهی اولیه با مقداردهی مکرر متغیر سطح بسته بعدی که از نظر ترتیب اعلان زودتر است و آماده مقداردهی اولیه است، ادامه مییابد تا زمانی که هیچ متغیر آمادهای برای مقداردهی اولیه وجود نداشته باشد.
اگر در پایان این فرآیند، متغیرهایی هنوز مقداردهی اولیه نشده باشند، آن متغیرها بخشی از یک یا چند چرخه مقداردهی اولیه هستند و برنامه معتبر نیست.
چندین متغیر در سمت چپ اعلان یک متغیر که توسط یک عبارت تک مقداری (چند مقداری) در سمت راست مقداردهی اولیه شدهاند، با هم مقداردهی اولیه میشوند: اگر هر یک از متغیرهای سمت چپ مقداردهی اولیه شوند، تمام آن متغیرها در همان مرحله مقداردهی اولیه میشوند.
var x = a
var a, b = f() // a and b are initialized together, before x is initializedترتیب اعلان متغیرهایی که در چندین فایل اعلان شدهاند، با ترتیب ارائه فایلها به کامپایلر تعیین میشود: متغیرهای اعلانشده در فایل اول، قبل از هر یک از متغیرهای اعلانشده در فایل دوم اعلان میشوند و به همین ترتیب ادامه مییابد. برای اطمینان از رفتار مقداردهی اولیه قابل تکرار، سیستمهای ساخت توصیه میشوند که چندین فایل متعلق به یک بسته را به ترتیب نام فایل لغوی به کامپایلر ارائه دهند.
تحلیل وابستگی به مقادیر واقعی متغیرها متکی نیست، بلکه فقط به ارجاعات لغوی به آنها در منبع، که به صورت متعدی تحلیل شدهاند، متکی است. برای مثال، اگر x عبارت مقداردهی اولیه یک متغیر به تابعی اشاره کند که بدنه آن به variable اشاره دارد، y آنگاه x به... وابسته است y. به طور خاص:
ارجاع به یک متغیر یا تابع، شناسهای است که آن متغیر یا تابع را مشخص میکند. ارجاع به یک متد، مقدار متد یا عبارت متد به شکل m است که در آن نوع (استاتیک) از نوع رابط نیست و متد در مجموعه متدهای است. فراخوانی شدن یا نشدن مقدار تابع حاصل اهمیتی ندارد.t.mtmtt.m یک متغیر، تابع یا متد در صورتی x به یک متغیر وابسته است که عبارت مقداردهی اولیه یا بدنه (برای توابع و متدها) شامل ارجاعی به یا به یک تابع یا متد وابسته به باشد.yxyy
ترتیب مقداردهی اولیه d، b، c، است a. توجه داشته باشید که ترتیب زیرعبارات در عبارات مقداردهی اولیه اهمیتی ندارد: a = c + b و a = b + c در این مثال منجر به همان ترتیب مقداردهی اولیه میشوند.
تحلیل وابستگی به ازای هر بسته انجام میشود؛ فقط ارجاعاتی که به متغیرها، توابع و متدهای (غیر رابطی) اعلام شده در بسته فعلی اشاره دارند، در نظر گرفته میشوند. اگر وابستگیهای دادهای پنهان دیگری بین متغیرها وجود داشته باشد، ترتیب مقداردهی اولیه بین آن متغیرها مشخص نمیشود.
var (
a = c + b // == 9
b = f() // == 4
c = f() // == 5
d = 3 // == 5 after initialization has finished
)
func f() int {
d++
return d
}متغیر a بعد از مقداردهی اولیه خواهد شد، b اما اینکه آیا x قبل b، بین b و a یا بعد از مقداردهی اولیه شده است a، و بنابراین لحظهای که sideEffect() فراخوانی میشود (قبل یا بعد x از مقداردهی اولیه) مشخص نشده است.
متغیرها همچنین میتوانند با استفاده از توابعی که init در بلوک پکیج تعریف شدهاند، بدون هیچ آرگومانی و بدون پارامتر نتیجه، مقداردهی اولیه شوند.
چندین تابع از این نوع میتوانند در هر بسته، حتی در یک فایل منبع واحد، تعریف شوند. در بلوک بسته، init شناسه فقط میتواند برای اعلان توابع استفاده شود init، اما خود شناسه اعلان نمیشود. بنابراین init توابع را نمیتوان از هیچ کجای برنامه به آنها ارجاع داد.
var x = I(T{}).ab() // x has an undetected, hidden dependency on a and b
var _ = sideEffect() // unrelated to x, a, or b
var a = b
var b = 42
type I interface { ab() []int }
type T struct{}
func (T) ab() []int { return []int{a, b} }کل بسته با اختصاص مقادیر اولیه به تمام متغیرهای سطح بسته، مقداردهی اولیه میشود و به دنبال آن تمام init توابع به ترتیبی که در منبع ظاهر میشوند، احتمالاً در چندین فایل، همانطور که به کامپایلر ارائه میشوند، فراخوانی میشوند.
func init() { … }بستههای یک برنامه کامل، گام به گام، یک به یک، مقداردهی اولیه میشوند. اگر یک بسته دارای import باشد، بستههای import شده قبل از مقداردهی اولیه خود بسته، مقداردهی اولیه میشوند. اگر چندین بسته یک بسته را import کنند، بسته import شده فقط یک بار مقداردهی اولیه میشود. import کردن بستهها، از نظر ساختاری، تضمین میکند که هیچ وابستگی مقداردهی اولیه چرخهای نمیتواند وجود داشته باشد. به طور دقیقتر:
با توجه به لیست تمام بستهها، که بر اساس مسیر واردات مرتب شدهاند، در هر مرحله، اولین بستهی مقداردهی نشده در لیست که تمام بستههای وارداتی (در صورت وجود) از قبل برای آن مقداردهی اولیه شدهاند، مقداردهی اولیه میشود. این مرحله تا زمانی که تمام بستهها مقداردهی اولیه شوند، تکرار میشود.
مقداردهی اولیه بسته - مقداردهی اولیه متغیر و فراخوانی init توابع - در یک goroutine واحد، به صورت متوالی، یک بسته در هر زمان اتفاق میافتد. یک init تابع ممکن است goroutine های دیگری را راهاندازی کند که میتوانند همزمان با کد مقداردهی اولیه اجرا شوند. با این حال، مقداردهی اولیه همیشه init توابع را به ترتیب انجام میدهد: تا زمانی که تابع قبلی بازگشته باشد، تابع بعدی را فراخوانی نمیکند.
یک برنامه کامل با پیوند دادن یک بسته منفرد و وارد نشده به نام بسته اصلی (main package) با تمام بستههایی که وارد میکند، به صورت انتقالی، ایجاد میشود. بسته اصلی باید نام بسته (package name) داشته باشد main و تابعی را تعریف کند main که هیچ آرگومانی نمیگیرد و هیچ مقداری را برنمیگرداند.
func main() { … }اجرای برنامه با مقداردهی اولیه برنامه و سپس فراخوانی تابع موجود main در بسته آغاز میشود. وقتی فراخوانی آن تابع بازگردد، برنامه خارج میشود. منتظر تکمیل شدن main سایر گوروتینها (غیر از) نمیماند.main
این رابط، رابط مرسوم برای نمایش وضعیت خطا است که در آن مقدار nil نشاندهندهی عدم وجود خطا است. برای مثال، میتوان تابعی برای خواندن دادهها از یک فایل تعریف کرد:
type error interface {
Error() string
}func Read(f *File, b []byte) (n int, err error)خطاهای اجرایی مانند تلاش برای اندیسگذاری یک آرایه خارج از محدوده، باعث ایجاد یک خطای زمان اجرا معادل فراخوانی تابع داخلی panic با مقداری از نوع رابط تعریفشده در پیادهسازی میشود runtime.Error. آن نوع، نوع رابط ازپیشاعلانشده را برآورده میکند error. مقادیر دقیق خطا که نشاندهنده شرایط خطای زمان اجرا هستند، مشخص نشدهاند.
package runtime
type Error interface {
error
// and perhaps other methods
}زمان اجرای بسته
بستهی داخلی unsafe که برای کامپایلر شناخته شده و از طریق مسیر import "unsafe" قابل دسترسی است، امکاناتی را برای برنامهنویسی سطح پایین، از جمله عملیاتی که سیستم نوع را نقض میکنند، فراهم میکند. یک بسته با استفاده از unsafe باید به صورت دستی از نظر ایمنی نوع بررسی شود و ممکن است قابل حمل نباشد. این بسته رابط زیر را ارائه میدهد:
package unsafe
type ArbitraryType int // shorthand for an arbitrary Go type; it is not a real type
type Pointer *ArbitraryType
func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr
type IntegerType int // shorthand for an integer type; it is not a real type
func Add(ptr Pointer, len IntegerType) Pointer
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
func SliceData(slice []ArbitraryType) *ArbitraryType
func String(ptr *byte, len IntegerType) string
func StringData(str string) *byte
اشارهگر نوع *نوع دلخواه
var f float64
bits = *(*uint64)(unsafe.Pointer(&f))
type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&f))
func f[P ~*B, B any](p P) uintptr {
return uintptr(unsafe.Pointer(p))
}
var p ptr = nilA یک نوع اشارهگر Pointer است، اما نمیتوان به یک مقدار ارجاع مجدد داد. هر اشارهگر یا مقداری از نوع زیرین را میتوان به نوعی از نوع زیرین تبدیل کرد و برعکس. اگر انواع مربوطه پارامترهای نوع باشند، همه انواع در مجموعههای نوع مربوطه باید نوع زیرین یکسانی داشته باشند که به ترتیب باید و باشد. تأثیر تبدیل بین و، تعریفشده توسط پیادهسازی است.Pointer uintptrPointeruintptrPointerPointeruintptr
توابع Alignofand Sizeof یک عبارت x از هر نوع را میگیرند و به ترتیب، تراز یا اندازه یک متغیر فرضی را برمیگردانند، v گویی v از طریق تعریف شدهاند var v = x.
uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
این تابع یک انتخابگر Offsetof (احتمالاً داخل پرانتز) میگیرد که نشاندهندهی فیلدی از ساختار است که با یا مشخص میشود و فاصلهی فیلد را بر حسب بایت نسبت به آدرس ساختار برمیگرداند. اگر یک فیلد تعبیهشده باشد، باید بدون اشارهگر غیرمستقیم از طریق فیلدهای ساختار قابل دسترسی باشد. برای ساختاری با فیلد: s.ffs*sfsf
uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
معماریهای کامپیوتر ممکن است نیاز به ترازبندی آدرسهای حافظه داشته باشند؛ یعنی برای اینکه آدرسهای یک متغیر مضربی از یک عامل باشند، ترازبندی نوع متغیر مورد نیاز است. این تابع Alignof عبارتی را میگیرد که نشاندهنده متغیری از هر نوع است و ترازبندی (نوع) متغیر را بر حسب بایت برمیگرداند. برای یک متغیر x:
یک (متغیر از) نوع، اگر یک پارامتر نوع باشد، یا اگر یک آرایه یا نوع ساختار باشد که شامل عناصر یا فیلدهایی با اندازه متغیر باشد، اندازه متغیر T دارد. در غیر این صورت، اندازه ثابت است. فراخوانیهای،، و عبارات ثابت زمان کامپایل از نوع هستند اگر آرگومانهای آنها (یا ساختار در عبارت انتخابگر برای) از نوعهایی با اندازه ثابت باشند.TAlignofOffsetofSizeofuintptrss.fOffsetof
تابع به اشارهگر بهروزرسانیشده [Go 1.17] Add اضافه شده len و آن را برمیگرداند. آرگومان باید از نوع عدد صحیح یا یک ثابت بدون نوع باشد. یک آرگومان ثابت باید با مقداری از نوع قابل نمایش باشد؛ اگر یک ثابت بدون نوع باشد، نوع آن مشخص میشود. قوانین مربوط به استفادههای معتبر از [] همچنان اعمال میشود.ptrunsafe.Pointer(uintptr(ptr) + uintptr(len))lenlenintintPointer
(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
این تابع Sliceslice ی را برمیگرداند که آرایه زیرین آن از شروع میشود ptr و طول و ظرفیت آن است len. Slice(ptr, len) معادل است با
به جز اینکه، به عنوان یک مورد خاص، اگر ptris nil و lenis صفر باشد، مقدار [Go 1.17Slice] را برمیگرداند.nil
آرگومان len باید از نوع عدد صحیح یا یک ثابت بدون نوع باشد. یک آرگومان ثابت len باید غیر منفی و قابل نمایش با مقداری از نوع باشد int؛ اگر یک ثابت بدون نوع باشد، به آن نوع داده میشود int. در زمان اجرا، اگر len منفی باشد، یا اگر ptr باشد nil و len صفر نباشد، یک خطای زمان اجرا رخ میدهد [Go 1.17].
این تابع SliceData یک اشارهگر به آرایهی زیرین آرگومان برمیگرداند slice. اگر ظرفیت slice cap(slice) صفر نباشد، آن اشارهگر برابر است با &slice[:1][0]. اگر slice برابر باشد nil، نتیجه برابر است با nil. در غیر این صورت، یک nil اشارهگر غیر- به یک آدرس حافظهی نامشخص است [Go 1.20].
این تابع مقداری String را برمیگرداند string که بایتهای زیرین آن از شروع میشوند ptr و طول آن است len. همان الزامات برای آرگومان ptrand len مانند تابع اعمال میشود Slice. اگر len صفر باشد، نتیجه رشته خالی است "". از آنجایی که رشتههای Go تغییرناپذیر هستند، بایتهای ارسالی به آن String نباید بعداً تغییر کنند. [Go 1.20]
این تابع StringData یک اشارهگر به بایتهای زیرین آرگومان برمیگرداند str. برای یک رشته خالی، مقدار برگشتی نامشخص است و میتواند باشد nil. از آنجایی که رشتههای Go تغییرناپذیر هستند، بایتهای برگشتی توسط [Go 1.20StringData] نباید تغییر کنند.
اندازه را بر حسب بایت تایپ کنید
type size in bytes
byte, uint8, int8 1
uint16, int16 2
uint32, int32, float32 4
uint64, int64, float64, complex64 8
complex128 16
بایت، uint8، int8 1 uint16، int16 2 uint32، int32، float32 4 uint64، int64، float64، complex64 8 کمپلکس۱۲۸ ۱۶ حداقل ویژگیهای همترازی زیر تضمین شده است:
برای متغیری x از نوع struct: unsafe.Alignof(x) بزرگترین مقدار از بین تمام مقادیر unsafe.Alignof(x.f) برای هر فیلد f از است x، اما حداقل ۱ است. یک نوع ساختار یا آرایه در صورتی اندازه صفر دارد که حاوی هیچ فیلد (یا عنصری) با اندازه بزرگتر از صفر نباشد. دو متغیر مجزا با اندازه صفر ممکن است آدرس یکسانی در حافظه داشته باشند.
ضمانت سازگاری Go 1 تضمین میکند که برنامههای نوشته شده با مشخصات Go 1، در طول عمر آن مشخصات، به درستی کامپایل و اجرا شوند و بدون تغییر باقی بمانند. به طور کلی، با انجام تنظیمات و اضافه شدن ویژگیهای جدید به زبان، ضمانت سازگاری تضمین میکند که یک برنامه Go که با یک نسخه خاص از زبان Go کار میکند، با هر نسخه بعدی نیز به کار خود ادامه دهد.
برای مثال، قابلیت استفاده از پیشوند 0b برای لیترالهای عدد صحیح دودویی با Go 1.13 معرفی شد که با [Go 1.13] در بخش لیترالهای عدد صحیح نشان داده شده است. کد منبعی که حاوی لیترال عدد صحیح مانند باشد، 0b1011 در صورتی که نسخه زبان ضمنی یا مورد نیاز مورد استفاده کامپایلر قدیمیتر از Go 1.13 باشد، رد خواهد شد.
جدول زیر حداقل نسخه زبان مورد نیاز برای ویژگیهای معرفی شده پس از Go 1 را شرح میدهد.
میتوان از اعلان نام مستعار برای اعلان یک نام مستعار برای یک نوع استفاده کرد.
لیترالهای عدد صحیح میتوانند از پیشوندهای 0b، 0B، 0o، و 0O به ترتیب برای لیترالهای دودویی و هشت هشتی استفاده کنند. لیترالهای ممیز شناور هگزادسیمال را میتوان با استفاده از پیشوندهای 0x و نوشت 0X. پسوند موهومی i میتواند با هر عدد صحیح (باینری، اعشاری، هگزادسیمال) یا عدد اعشاری استفاده شود، نه فقط با عدد اعشاری. ارقام هر عدد حقیقی را میتوان با استفاده از زیرخط (_) از هم جدا کرد (گروهبندی کرد) _. تعداد شیفت در یک عملیات شیفت میتواند از نوع عدد صحیح علامتدار باشد.
جاسازی یک متد بیش از یک بار از طریق رابطهای تعبیهشدهی مختلف، خطا محسوب نمیشود.
اگر نوع عناصر یک slice و آرایه با هم مطابقت داشته باشند و آرایه از slice بزرگتر نباشد، میتوان آن را به یک اشارهگر آرایه تبدیل کرد.
نسخه ۱.۱۸ توابع و انواع چندریختی ("ژنریکها") را به زبان اضافه میکند. به طور خاص:
مجموعه عملگرها و علائم نگارشی شامل توکن جدید میشود ~. اعلانهای تابع و نوع ممکن است پارامترهای نوع را اعلان کنند. انواع رابطها میتوانند انواع دلخواه (نه فقط نام نوع رابطها) و همچنین ~T عناصر اتحاد و نوع را در خود جای دهند. مجموعه انواع از پیش تعریف شده شامل انواع جدید any و comparable.
اگر نوع عناصر یک slice و آرایه با هم مطابقت داشته باشند و آرایه از slice بزرگتر نباشد، میتوان آن را به آرایه تبدیل کرد. انواع قابل مقایسه (مانند رابطهای معمولی) ممکن است comparable محدودیتها را برآورده کنند، حتی اگر آرگومانهای نوع کاملاً قابل مقایسه نباشند.
مجموعه توابع از پیش تعریف شده شامل توابع جدید min، max و است clear. استنتاج نوع از انواع روشهای رابط برای استنتاج استفاده میکند. همچنین آرگومانهای نوع را برای توابع عمومی اختصاص داده شده به متغیرها یا ارسال شده به عنوان آرگومان به سایر توابع (احتمالاً عمومی) استنباط میکند.
در یک دستور "for"، هر تکرار به جای اینکه متغیرهای یکسانی را در هر تکرار به اشتراک بگذارد، مجموعهای از متغیرهای تکرار مخصوص به خود را دارد. یک دستور "for" با بند "range" میتواند روی مقادیر صحیح از صفر تا یک حد بالا تکرار کند.
یک دستور "for" با بند "range" یک تابع تکرارکننده را به عنوان عبارت محدوده میپذیرد.
یک اعلان نام مستعار ممکن است پارامترهای نوع را اعلان کند.
قوانین یکسانسازی نوع، چگونگی و نحوهی یکی شدن دو نوع را شرح میدهند. جزئیات دقیق برای پیادهسازیهای Go مرتبط هستند، بر جزئیات پیامهای خطا (مانند اینکه آیا کامپایلر یک استنتاج نوع یا خطای دیگری را گزارش میدهد) تأثیر میگذارند و ممکن است توضیح دهند که چرا استنتاج نوع در موقعیتهای غیرمعمول کد با شکست مواجه میشود. اما روی هم رفته، این قوانین را میتوان هنگام نوشتن کد Go نادیده گرفت: استنتاج نوع طوری طراحی شده است که عمدتاً "مطابق انتظار کار کند" و قوانین یکسانسازی بر این اساس تنظیم دقیق میشوند.
یکسانسازی نوع توسط یک حالت تطبیق کنترل میشود که میتواند دقیق یا آزاد باشد. از آنجایی که یکسانسازی به صورت بازگشتی در یک ساختار نوع مرکب انجام میشود، حالت تطبیق مورد استفاده برای عناصر آن نوع، یعنی حالت تطبیق عنصر، همان حالت تطبیق باقی میماند، مگر زمانی که دو نوع برای قابلیت تخصیص () یکسان شوند: در این حالت، حالت تطبیق در سطح بالا آزاد است اما سپس برای انواع عناصر به دقیق تغییر میکند، که نشان دهنده این واقعیت است که انواع برای قابلیت تخصیص لازم نیست یکسان باشند.≡A
دو نوع که پارامترهای نوع مقید ندارند، در صورت درست بودن هر یک از شرایط زیر، دقیقاً یکسان میشوند:
هر دو نوع یکسان هستند. هر دو نوع ساختار یکسانی دارند و نوع عناصر آنها دقیقاً یکسان است. دقیقاً یک نوع، پارامتر نوع نامحدود است و تمام انواع موجود در مجموعه نوع آن، طبق قوانین یکسانسازی (یکپارچهسازی آزاد در سطح بالا و یکپارچهسازی دقیق برای انواع عناصر) با نوع دیگر یکسانسازی میشوند.≡A اگر هر دو نوع، پارامترهای نوع مقید باشند، در صورت وجود شرایط تطبیق داده شده، با هم یکی میشوند:
پارامترهای هر دو نوع یکسان هستند. حداکثر یکی از پارامترهای نوع، آرگومان نوع شناختهشدهای دارد. در این حالت، پارامترهای نوع به هم متصل میشوند: هر دو نشاندهندهی آرگومان نوع یکسانی هستند. اگر هیچکدام از پارامترهای نوع هنوز آرگومان نوع شناختهشدهای نداشته باشند، یک آرگومان نوع آینده که برای یکی از پارامترهای نوع استنباط میشود، بهطور همزمان برای هر دوی آنها استنباط میشود. هر دو پارامتر نوع، یک آرگومان نوع شناختهشده دارند و آرگومانهای نوع بر اساس حالتهای تطبیق داده شده، یکی میشوند. یک پارامتر نوع تکمحدود P و یک نوع دیگر T در حالتهای تطبیق داده شده یکی میشوند اگر:
P آرگومان نوع شناختهشدهای ندارد. در این مورد، T به عنوان آرگومان نوع برای استنباط میشود P. P یک آرگومان از نوع شناختهشده دارد A و بر اساس حالتهای تطبیق دادهشده، unify میکند و یکی از شرایط زیر صحیح است A:T هر دو A و T از نوع رابط هستند: در این حالت، اگر هر دو A و نیز از نوع تعریفشده T باشند، باید یکسان باشند. در غیر این صورت، اگر هیچکدام از آنها نوع تعریفشدهای نباشند، باید تعداد متدهای یکسانی داشته باشند (یکپارچهسازی و از قبل مشخص شده است که متدها با هم مطابقت دارند).AT نه نوع رابط A و نه T نوع رابط: در این حالت، if T یک نوع تعریف شده است، به عنوان آرگومان نوع استنباط شده برای T جایگزین میشود.AP در نهایت، دو نوع که پارامترهای نوع مقید ندارند، اگر موارد زیر را داشته باشند، به صورت آزاد (و بر اساس حالت تطبیق عنصر) یکی میشوند:
هر دو نوع دقیقاً با هم یکی میشوند. یک نوع، نوع تعریفشده است، نوع دیگر یک نوع تحتاللفظی است، اما رابط نیست و انواع زیربنایی آنها بر اساس حالت تطبیق عنصر، یکسان میشوند. هر دو نوع، رابطهایی (اما نه پارامترهای نوع) با اصطلاحات نوع یکسان هستند، هر دو یا هیچکدام نوع ازپیشاعلانشده را قابل مقایسه جاسازی نمیکنند، انواع متدهای مربوطه دقیقاً یکسان میشوند و مجموعه متد یکی از رابطها زیرمجموعهای از مجموعه متد رابط دیگر است. فقط یک نوع، رابط است (اما پارامتر نوع نیست)، متدهای متناظر دو نوع، بر اساس حالت تطبیق عنصر، یکی میشوند و مجموعه متدهای رابط، زیرمجموعهای از مجموعه متدهای نوع دیگر است. هر دو نوع ساختار یکسانی دارند و نوع المانهای آنها بر اساس حالت تطبیق المان، یکسان میشود.