[{"data":1,"prerenderedAt":4270},["ShallowReactive",2],{"content-\u002F01-basics\u002F05-interfaces":3},{"id":4,"title":5,"body":6,"description":4252,"difficulty":4253,"extension":4254,"meta":4255,"module":4256,"navigation":88,"next":4257,"order":92,"path":4258,"prev":4259,"seo":4260,"slug":4261,"stem":4262,"tags":4263,"__hash__":4269},"content\u002F01-basics\u002F05-interfaces\u002Findex.md","Интерфейсы в Go",{"type":7,"value":8,"toc":4232},"minimark",[9,14,28,31,36,232,235,238,291,294,296,300,307,320,370,382,386,492,504,516,518,522,532,600,602,606,609,746,753,768,770,774,782,810,913,942,947,1113,1115,1119,1122,1267,1276,1278,1282,1285,1496,1502,1504,1508,1511,1536,1543,1546,1720,1722,1726,1730,1733,1865,1868,1872,1879,2085,2088,2092,2363,2365,2369,2381,2403,2426,2441,2461,2475,2489,2497,2505,2527,2529,2533,2535,2540,2546,2552,2576,2615,2620,2628,2633,3069,3071,3076,3081,3086,3091,3421,3426,3432,3436,3513,3515,3520,3525,3530,3551,3587,3591,3597,3601,4226,4228],[10,11,13],"h1",{"id":12},"интерфейс-в-go","Интерфейс в Go",[15,16,17,18,22,23,27],"p",{},"Интерфейсы — это то, что делает Go-код по-настоящему гибким. В большинстве языков для реализации интерфейса нужно явно написать ",[19,20,21],"code",{},"implements",". В Go достаточно просто иметь нужные методы — это называется ",[24,25,26],"strong",{},"structural typing"," или duck typing. За этой простотой снаружи скрывается нетривиальное устройство внутри, которое важно понимать, чтобы не попасть в классические ловушки.",[29,30],"hr",{},[32,33,35],"h2",{"id":34},"объявление-и-неявная-реализация","Объявление и неявная реализация",[37,38,43],"pre",{"className":39,"code":40,"language":41,"meta":42,"style":42},"language-go shiki shiki-themes github-dark","type Stringer interface {\n    String() string\n}\n\ntype User struct {\n    Name string\n}\n\n\u002F\u002F User реализует Stringer — просто наличием метода\n\u002F\u002F Никакого \"implements Stringer\" не нужно\nfunc (u User) String() string {\n    return u.Name\n}\n\nvar s Stringer = User{Name: \"Alice\"} \u002F\u002F работает\nfmt.Println(s.String())              \u002F\u002F \"Alice\"\n","go","",[19,44,45,65,77,83,90,103,111,116,121,128,134,163,172,177,182,212],{"__ignoreMap":42},[46,47,50,54,58,61],"span",{"class":48,"line":49},"line",1,[46,51,53],{"class":52},"snl16","type",[46,55,57],{"class":56},"svObZ"," Stringer",[46,59,60],{"class":52}," interface",[46,62,64],{"class":63},"s95oV"," {\n",[46,66,68,71,74],{"class":48,"line":67},2,[46,69,70],{"class":56},"    String",[46,72,73],{"class":63},"() ",[46,75,76],{"class":52},"string\n",[46,78,80],{"class":48,"line":79},3,[46,81,82],{"class":63},"}\n",[46,84,86],{"class":48,"line":85},4,[46,87,89],{"emptyLinePlaceholder":88},true,"\n",[46,91,93,95,98,101],{"class":48,"line":92},5,[46,94,53],{"class":52},[46,96,97],{"class":56}," User",[46,99,100],{"class":52}," struct",[46,102,64],{"class":63},[46,104,106,109],{"class":48,"line":105},6,[46,107,108],{"class":63},"    Name ",[46,110,76],{"class":52},[46,112,114],{"class":48,"line":113},7,[46,115,82],{"class":63},[46,117,119],{"class":48,"line":118},8,[46,120,89],{"emptyLinePlaceholder":88},[46,122,124],{"class":48,"line":123},9,[46,125,127],{"class":126},"sAwPA","\u002F\u002F User реализует Stringer — просто наличием метода\n",[46,129,131],{"class":48,"line":130},10,[46,132,133],{"class":126},"\u002F\u002F Никакого \"implements Stringer\" не нужно\n",[46,135,137,140,143,147,150,153,156,158,161],{"class":48,"line":136},11,[46,138,139],{"class":52},"func",[46,141,142],{"class":63}," (",[46,144,146],{"class":145},"s9osk","u ",[46,148,149],{"class":56},"User",[46,151,152],{"class":63},") ",[46,154,155],{"class":56},"String",[46,157,73],{"class":63},[46,159,160],{"class":52},"string",[46,162,64],{"class":63},[46,164,166,169],{"class":48,"line":165},12,[46,167,168],{"class":52},"    return",[46,170,171],{"class":63}," u.Name\n",[46,173,175],{"class":48,"line":174},13,[46,176,82],{"class":63},[46,178,180],{"class":48,"line":179},14,[46,181,89],{"emptyLinePlaceholder":88},[46,183,185,188,191,194,197,199,202,206,209],{"class":48,"line":184},15,[46,186,187],{"class":52},"var",[46,189,190],{"class":63}," s ",[46,192,193],{"class":56},"Stringer",[46,195,196],{"class":52}," =",[46,198,97],{"class":56},[46,200,201],{"class":63},"{Name: ",[46,203,205],{"class":204},"sU2Wk","\"Alice\"",[46,207,208],{"class":63},"} ",[46,210,211],{"class":126},"\u002F\u002F работает\n",[46,213,215,218,221,224,226,229],{"class":48,"line":214},16,[46,216,217],{"class":63},"fmt.",[46,219,220],{"class":56},"Println",[46,222,223],{"class":63},"(s.",[46,225,155],{"class":56},[46,227,228],{"class":63},"())              ",[46,230,231],{"class":126},"\u002F\u002F \"Alice\"\n",[15,233,234],{},"Компилятор проверяет соответствие интерфейсу в момент присвоения. Если метода нет — ошибка компиляции, а не рантайм.",[15,236,237],{},"Чтобы явно проверить реализацию во время компиляции — используют blank identifier:",[37,239,241],{"className":39,"code":240,"language":41,"meta":42,"style":42},"var _ Stringer = User{}     \u002F\u002F если User не реализует Stringer — ошибка компиляции\nvar _ Stringer = (*User)(nil) \u002F\u002F проверка для pointer receiver\n",[19,242,243,262],{"__ignoreMap":42},[46,244,245,247,250,252,254,256,259],{"class":48,"line":49},[46,246,187],{"class":52},[46,248,249],{"class":63}," _ ",[46,251,193],{"class":56},[46,253,196],{"class":52},[46,255,97],{"class":56},[46,257,258],{"class":63},"{}     ",[46,260,261],{"class":126},"\u002F\u002F если User не реализует Stringer — ошибка компиляции\n",[46,263,264,266,268,270,272,274,277,279,282,286,288],{"class":48,"line":67},[46,265,187],{"class":52},[46,267,249],{"class":63},[46,269,193],{"class":56},[46,271,196],{"class":52},[46,273,142],{"class":63},[46,275,276],{"class":52},"*",[46,278,149],{"class":56},[46,280,281],{"class":63},")(",[46,283,285],{"class":284},"sDLfK","nil",[46,287,152],{"class":63},[46,289,290],{"class":126},"\u002F\u002F проверка для pointer receiver\n",[15,292,293],{},"Это идиоматичный способ зафиксировать намерение и получить ошибку сразу, а не в момент использования.",[29,295],{},[32,297,299],{"id":298},"внутреннее-устройство-iface-и-eface","Внутреннее устройство: iface и eface",[15,301,302,303,306],{},"Переменная интерфейсного типа — это не просто указатель на данные. Под капотом это ",[24,304,305],{},"два слова"," (два указателя по 8 байт на 64-битной платформе). Конкретная структура зависит от того, пустой интерфейс или нет.",[308,309,311,312,315,316,319],"h3",{"id":310},"eface-пустой-интерфейс-interface-any","eface — пустой интерфейс (",[19,313,314],{},"interface{}"," \u002F ",[19,317,318],{},"any",")",[37,321,323],{"className":39,"code":322,"language":41,"meta":42,"style":42},"type eface struct {\n    _type *_type  \u002F\u002F указатель на описание типа\n    data  unsafe.Pointer \u002F\u002F указатель на данные\n}\n",[19,324,325,336,349,366],{"__ignoreMap":42},[46,326,327,329,332,334],{"class":48,"line":49},[46,328,53],{"class":52},[46,330,331],{"class":56}," eface",[46,333,100],{"class":52},[46,335,64],{"class":63},[46,337,338,341,343,346],{"class":48,"line":67},[46,339,340],{"class":63},"    _type ",[46,342,276],{"class":52},[46,344,345],{"class":56},"_type",[46,347,348],{"class":126},"  \u002F\u002F указатель на описание типа\n",[46,350,351,354,357,360,363],{"class":48,"line":79},[46,352,353],{"class":63},"    data  ",[46,355,356],{"class":56},"unsafe",[46,358,359],{"class":63},".",[46,361,362],{"class":56},"Pointer",[46,364,365],{"class":126}," \u002F\u002F указатель на данные\n",[46,367,368],{"class":48,"line":85},[46,369,82],{"class":63},[15,371,372,375,376,378,379,381],{},[19,373,374],{},"eface"," используется для ",[19,377,314],{}," (он же ",[19,380,318],{}," начиная с Go 1.18). Хранит только тип и данные — никакой таблицы методов не нужно, потому что через пустой интерфейс методы не вызываются.",[308,383,385],{"id":384},"iface-непустой-интерфейс","iface — непустой интерфейс",[37,387,389],{"className":39,"code":388,"language":41,"meta":42,"style":42},"type iface struct {\n    tab  *itab          \u002F\u002F указатель на таблицу методов (itab)\n    data unsafe.Pointer \u002F\u002F указатель на данные\n}\n\ntype itab struct {\n    inter *interfacetype \u002F\u002F описание интерфейса\n    _type *_type         \u002F\u002F описание конкретного типа\n    fun   [1]uintptr     \u002F\u002F таблица методов (variable size)\n}\n",[19,390,391,402,415,428,432,436,447,460,471,488],{"__ignoreMap":42},[46,392,393,395,398,400],{"class":48,"line":49},[46,394,53],{"class":52},[46,396,397],{"class":56}," iface",[46,399,100],{"class":52},[46,401,64],{"class":63},[46,403,404,407,409,412],{"class":48,"line":67},[46,405,406],{"class":63},"    tab  ",[46,408,276],{"class":52},[46,410,411],{"class":56},"itab",[46,413,414],{"class":126},"          \u002F\u002F указатель на таблицу методов (itab)\n",[46,416,417,420,422,424,426],{"class":48,"line":79},[46,418,419],{"class":63},"    data ",[46,421,356],{"class":56},[46,423,359],{"class":63},[46,425,362],{"class":56},[46,427,365],{"class":126},[46,429,430],{"class":48,"line":85},[46,431,82],{"class":63},[46,433,434],{"class":48,"line":92},[46,435,89],{"emptyLinePlaceholder":88},[46,437,438,440,443,445],{"class":48,"line":105},[46,439,53],{"class":52},[46,441,442],{"class":56}," itab",[46,444,100],{"class":52},[46,446,64],{"class":63},[46,448,449,452,454,457],{"class":48,"line":113},[46,450,451],{"class":63},"    inter ",[46,453,276],{"class":52},[46,455,456],{"class":56},"interfacetype",[46,458,459],{"class":126}," \u002F\u002F описание интерфейса\n",[46,461,462,464,466,468],{"class":48,"line":118},[46,463,340],{"class":63},[46,465,276],{"class":52},[46,467,345],{"class":56},[46,469,470],{"class":126},"         \u002F\u002F описание конкретного типа\n",[46,472,473,476,479,482,485],{"class":48,"line":123},[46,474,475],{"class":63},"    fun   [",[46,477,478],{"class":284},"1",[46,480,481],{"class":63},"]",[46,483,484],{"class":52},"uintptr",[46,486,487],{"class":126},"     \u002F\u002F таблица методов (variable size)\n",[46,489,490],{"class":48,"line":130},[46,491,82],{"class":63},[15,493,494,497,498,500,501,359],{},[19,495,496],{},"iface"," используется для любого непустого интерфейса. ",[19,499,411],{}," — это кешируемая структура: Go не строит её заново при каждом присвоении, а хранит в глобальном кеше по паре ",[19,502,503],{},"(interface, concrete type)",[15,505,506,509,510,512,513,515],{},[24,507,508],{},"Главное различие в одну строку:"," ",[19,511,374],{}," — два слова (тип + данные), ",[19,514,496],{}," — два слова (itab + данные), где itab содержит и тип, и таблицу методов для диспетчеризации вызовов.",[29,517],{},[32,519,521],{"id":520},"как-данные-попадают-в-интерфейс","Как данные попадают в интерфейс",[15,523,524,525,528,529,531],{},"Если значение помещается в машинное слово (≤ 8 байт) — оно может храниться прямо в поле ",[19,526,527],{},"data",". Если больше — аллоцируется в куче, ",[19,530,527],{}," хранит указатель. Это важно с точки зрения производительности: упаковка значений в интерфейс может вызывать аллокации:",[37,533,535],{"className":39,"code":534,"language":41,"meta":42,"style":42},"var s Stringer\n\n\u002F\u002F Аллокация: User > 8 байт, данные уходят в кучу\ns = User{Name: \"Alice\"}\n\n\u002F\u002F Можно избежать, если работать с указателем\ns = &User{Name: \"Alice\"} \u002F\u002F data хранит указатель — одна аллокация в любом случае\n",[19,536,537,546,550,555,571,575,580],{"__ignoreMap":42},[46,538,539,541,543],{"class":48,"line":49},[46,540,187],{"class":52},[46,542,190],{"class":63},[46,544,545],{"class":56},"Stringer\n",[46,547,548],{"class":48,"line":67},[46,549,89],{"emptyLinePlaceholder":88},[46,551,552],{"class":48,"line":79},[46,553,554],{"class":126},"\u002F\u002F Аллокация: User > 8 байт, данные уходят в кучу\n",[46,556,557,560,563,565,567,569],{"class":48,"line":85},[46,558,559],{"class":63},"s ",[46,561,562],{"class":52},"=",[46,564,97],{"class":56},[46,566,201],{"class":63},[46,568,205],{"class":204},[46,570,82],{"class":63},[46,572,573],{"class":48,"line":92},[46,574,89],{"emptyLinePlaceholder":88},[46,576,577],{"class":48,"line":105},[46,578,579],{"class":126},"\u002F\u002F Можно избежать, если работать с указателем\n",[46,581,582,584,586,589,591,593,595,597],{"class":48,"line":113},[46,583,559],{"class":63},[46,585,562],{"class":52},[46,587,588],{"class":52}," &",[46,590,149],{"class":56},[46,592,201],{"class":63},[46,594,205],{"class":204},[46,596,208],{"class":63},[46,598,599],{"class":126},"\u002F\u002F data хранит указатель — одна аллокация в любом случае\n",[29,601],{},[32,603,605],{"id":604},"composition-интерфейсы-из-интерфейсов","Composition — интерфейсы из интерфейсов",[15,607,608],{},"Интерфейсы можно компоновать из других интерфейсов:",[37,610,612],{"className":39,"code":611,"language":41,"meta":42,"style":42},"type Reader interface {\n    Read(p []byte) (n int, err error)\n}\n\ntype Writer interface {\n    Write(p []byte) (n int, err error)\n}\n\n\u002F\u002F io.ReadWriter — стандартный пример из stdlib\ntype ReadWriter interface {\n    Reader\n    Writer\n}\n",[19,613,614,625,662,666,670,681,708,712,716,721,732,737,742],{"__ignoreMap":42},[46,615,616,618,621,623],{"class":48,"line":49},[46,617,53],{"class":52},[46,619,620],{"class":56}," Reader",[46,622,60],{"class":52},[46,624,64],{"class":63},[46,626,627,630,633,635,638,641,644,647,650,653,656,659],{"class":48,"line":67},[46,628,629],{"class":56},"    Read",[46,631,632],{"class":63},"(",[46,634,15],{"class":145},[46,636,637],{"class":63}," []",[46,639,640],{"class":52},"byte",[46,642,643],{"class":63},") (",[46,645,646],{"class":145},"n",[46,648,649],{"class":52}," int",[46,651,652],{"class":63},", ",[46,654,655],{"class":145},"err",[46,657,658],{"class":52}," error",[46,660,661],{"class":63},")\n",[46,663,664],{"class":48,"line":79},[46,665,82],{"class":63},[46,667,668],{"class":48,"line":85},[46,669,89],{"emptyLinePlaceholder":88},[46,671,672,674,677,679],{"class":48,"line":92},[46,673,53],{"class":52},[46,675,676],{"class":56}," Writer",[46,678,60],{"class":52},[46,680,64],{"class":63},[46,682,683,686,688,690,692,694,696,698,700,702,704,706],{"class":48,"line":105},[46,684,685],{"class":56},"    Write",[46,687,632],{"class":63},[46,689,15],{"class":145},[46,691,637],{"class":63},[46,693,640],{"class":52},[46,695,643],{"class":63},[46,697,646],{"class":145},[46,699,649],{"class":52},[46,701,652],{"class":63},[46,703,655],{"class":145},[46,705,658],{"class":52},[46,707,661],{"class":63},[46,709,710],{"class":48,"line":113},[46,711,82],{"class":63},[46,713,714],{"class":48,"line":118},[46,715,89],{"emptyLinePlaceholder":88},[46,717,718],{"class":48,"line":123},[46,719,720],{"class":126},"\u002F\u002F io.ReadWriter — стандартный пример из stdlib\n",[46,722,723,725,728,730],{"class":48,"line":130},[46,724,53],{"class":52},[46,726,727],{"class":56}," ReadWriter",[46,729,60],{"class":52},[46,731,64],{"class":63},[46,733,734],{"class":48,"line":136},[46,735,736],{"class":56},"    Reader\n",[46,738,739],{"class":48,"line":165},[46,740,741],{"class":56},"    Writer\n",[46,743,744],{"class":48,"line":174},[46,745,82],{"class":63},[15,747,748,749,752],{},"Тип, реализующий ",[19,750,751],{},"ReadWriter",", должен иметь оба метода. Это основа модульности в Go: маленькие интерфейсы из одного-двух методов, которые собираются в более крупные по необходимости.",[754,755,756],"blockquote",{},[15,757,758,759,763,764,767],{},"Знаменитая цитата из Go-сообщества: ",[760,761,762],"em",{},"\"The bigger the interface, the weaker the abstraction\""," — Роб Пайк. ",[19,765,766],{},"io.Reader"," с одним методом применим в тысячах мест. Интерфейс из 15 методов — почти нигде.",[29,769],{},[32,771,773],{"id":772},"nil-interface-главная-ловушка","nil interface — главная ловушка",[15,775,776,777,779,780,359],{},"Это одна из самых известных ловушек в Go, и она прямо следует из устройства ",[19,778,496],{},"\u002F",[19,781,374],{},[15,783,784,785,787,788,142,791,779,794,796,797,799,800,802,803,805,806,809],{},"Интерфейс равен ",[19,786,285],{}," только если ",[24,789,790],{},"оба поля",[19,792,793],{},"tab",[19,795,345],{}," и ",[19,798,527],{},") равны ",[19,801,285],{},". Если тип задан, а данные ",[19,804,285],{}," — интерфейс ",[24,807,808],{},"не nil",":",[37,811,813],{"className":39,"code":812,"language":41,"meta":42,"style":42},"func getError() error {\n    var p *MyError = nil  \u002F\u002F typed nil pointer\n    return p              \u002F\u002F УПАКОВЫВАЕТСЯ в iface: tab=*MyError, data=nil\n}\n\nerr := getError()\nfmt.Println(err == nil)  \u002F\u002F false (!!)\nfmt.Println(err)         \u002F\u002F \u003Cnil> — выглядит как nil, но не nil\n",[19,814,815,829,850,860,864,868,881,901],{"__ignoreMap":42},[46,816,817,819,822,824,827],{"class":48,"line":49},[46,818,139],{"class":52},[46,820,821],{"class":56}," getError",[46,823,73],{"class":63},[46,825,826],{"class":52},"error",[46,828,64],{"class":63},[46,830,831,834,837,839,842,844,847],{"class":48,"line":67},[46,832,833],{"class":52},"    var",[46,835,836],{"class":63}," p ",[46,838,276],{"class":52},[46,840,841],{"class":56},"MyError",[46,843,196],{"class":52},[46,845,846],{"class":284}," nil",[46,848,849],{"class":126},"  \u002F\u002F typed nil pointer\n",[46,851,852,854,857],{"class":48,"line":79},[46,853,168],{"class":52},[46,855,856],{"class":63}," p              ",[46,858,859],{"class":126},"\u002F\u002F УПАКОВЫВАЕТСЯ в iface: tab=*MyError, data=nil\n",[46,861,862],{"class":48,"line":85},[46,863,82],{"class":63},[46,865,866],{"class":48,"line":92},[46,867,89],{"emptyLinePlaceholder":88},[46,869,870,873,876,878],{"class":48,"line":105},[46,871,872],{"class":63},"err ",[46,874,875],{"class":52},":=",[46,877,821],{"class":56},[46,879,880],{"class":63},"()\n",[46,882,883,885,887,890,893,895,898],{"class":48,"line":113},[46,884,217],{"class":63},[46,886,220],{"class":56},[46,888,889],{"class":63},"(err ",[46,891,892],{"class":52},"==",[46,894,846],{"class":284},[46,896,897],{"class":63},")  ",[46,899,900],{"class":126},"\u002F\u002F false (!!)\n",[46,902,903,905,907,910],{"class":48,"line":118},[46,904,217],{"class":63},[46,906,220],{"class":56},[46,908,909],{"class":63},"(err)         ",[46,911,912],{"class":126},"\u002F\u002F \u003Cnil> — выглядит как nil, но не nil\n",[15,914,915,916,918,919,922,923,925,926,928,929,931,932,934,935,937,938,941],{},"Что происходит: ",[19,917,15],{}," — это ",[19,920,921],{},"*MyError"," со значением ",[19,924,285],{},". При возврате через ",[19,927,826],{}," (интерфейс) Go создаёт ",[19,930,496],{}," с ",[19,933,793],{},", указывающим на ",[19,936,921],{},", и ",[19,939,940],{},"data=nil",". Интерфейс ненулевой, потому что тип задан.",[15,943,944],{},[24,945,946],{},"Правильный способ вернуть nil error:",[37,948,950],{"className":39,"code":949,"language":41,"meta":42,"style":42},"\u002F\u002F Плохо\nfunc validate(s string) error {\n    var err *ValidationError\n    if s == \"\" {\n        err = &ValidationError{\"empty\"}\n    }\n    return err \u002F\u002F всегда ненулевой error!\n}\n\n\u002F\u002F Хорошо\nfunc validate(s string) error {\n    if s == \"\" {\n        return &ValidationError{\"empty\"}\n    }\n    return nil \u002F\u002F возвращаем nil интерфейс, не typed nil\n}\n",[19,951,952,957,978,990,1004,1024,1029,1038,1042,1046,1051,1069,1081,1096,1100,1109],{"__ignoreMap":42},[46,953,954],{"class":48,"line":49},[46,955,956],{"class":126},"\u002F\u002F Плохо\n",[46,958,959,961,964,966,969,972,974,976],{"class":48,"line":67},[46,960,139],{"class":52},[46,962,963],{"class":56}," validate",[46,965,632],{"class":63},[46,967,968],{"class":145},"s",[46,970,971],{"class":52}," string",[46,973,152],{"class":63},[46,975,826],{"class":52},[46,977,64],{"class":63},[46,979,980,982,985,987],{"class":48,"line":79},[46,981,833],{"class":52},[46,983,984],{"class":63}," err ",[46,986,276],{"class":52},[46,988,989],{"class":56},"ValidationError\n",[46,991,992,995,997,999,1002],{"class":48,"line":85},[46,993,994],{"class":52},"    if",[46,996,190],{"class":63},[46,998,892],{"class":52},[46,1000,1001],{"class":204}," \"\"",[46,1003,64],{"class":63},[46,1005,1006,1009,1011,1013,1016,1019,1022],{"class":48,"line":92},[46,1007,1008],{"class":63},"        err ",[46,1010,562],{"class":52},[46,1012,588],{"class":52},[46,1014,1015],{"class":56},"ValidationError",[46,1017,1018],{"class":63},"{",[46,1020,1021],{"class":204},"\"empty\"",[46,1023,82],{"class":63},[46,1025,1026],{"class":48,"line":105},[46,1027,1028],{"class":63},"    }\n",[46,1030,1031,1033,1035],{"class":48,"line":113},[46,1032,168],{"class":52},[46,1034,984],{"class":63},[46,1036,1037],{"class":126},"\u002F\u002F всегда ненулевой error!\n",[46,1039,1040],{"class":48,"line":118},[46,1041,82],{"class":63},[46,1043,1044],{"class":48,"line":123},[46,1045,89],{"emptyLinePlaceholder":88},[46,1047,1048],{"class":48,"line":130},[46,1049,1050],{"class":126},"\u002F\u002F Хорошо\n",[46,1052,1053,1055,1057,1059,1061,1063,1065,1067],{"class":48,"line":136},[46,1054,139],{"class":52},[46,1056,963],{"class":56},[46,1058,632],{"class":63},[46,1060,968],{"class":145},[46,1062,971],{"class":52},[46,1064,152],{"class":63},[46,1066,826],{"class":52},[46,1068,64],{"class":63},[46,1070,1071,1073,1075,1077,1079],{"class":48,"line":165},[46,1072,994],{"class":52},[46,1074,190],{"class":63},[46,1076,892],{"class":52},[46,1078,1001],{"class":204},[46,1080,64],{"class":63},[46,1082,1083,1086,1088,1090,1092,1094],{"class":48,"line":174},[46,1084,1085],{"class":52},"        return",[46,1087,588],{"class":52},[46,1089,1015],{"class":56},[46,1091,1018],{"class":63},[46,1093,1021],{"class":204},[46,1095,82],{"class":63},[46,1097,1098],{"class":48,"line":179},[46,1099,1028],{"class":63},[46,1101,1102,1104,1106],{"class":48,"line":184},[46,1103,168],{"class":52},[46,1105,846],{"class":284},[46,1107,1108],{"class":126}," \u002F\u002F возвращаем nil интерфейс, не typed nil\n",[46,1110,1111],{"class":48,"line":214},[46,1112,82],{"class":63},[29,1114],{},[32,1116,1118],{"id":1117},"type-assertion","Type assertion",[15,1120,1121],{},"Type assertion — это способ достать конкретный тип из интерфейса:",[37,1123,1125],{"className":39,"code":1124,"language":41,"meta":42,"style":42},"var s Stringer = User{Name: \"Alice\"}\n\n\u002F\u002F Одноаргументная форма — паника если тип не совпал\nu := s.(User)\nfmt.Println(u.Name) \u002F\u002F \"Alice\"\n\n\u002F\u002F Двухаргументная форма — безопасно\nu, ok := s.(User)\nif ok {\n    fmt.Println(u.Name)\n}\n\n\u002F\u002F Частая ошибка: assertion к неправильному типу\nvar s Stringer = User{Name: \"Alice\"}\n_, ok := s.(*User) \u002F\u002F false — s содержит User, не *User\n",[19,1126,1127,1145,1149,1154,1167,1178,1182,1187,1200,1208,1218,1222,1226,1231,1249],{"__ignoreMap":42},[46,1128,1129,1131,1133,1135,1137,1139,1141,1143],{"class":48,"line":49},[46,1130,187],{"class":52},[46,1132,190],{"class":63},[46,1134,193],{"class":56},[46,1136,196],{"class":52},[46,1138,97],{"class":56},[46,1140,201],{"class":63},[46,1142,205],{"class":204},[46,1144,82],{"class":63},[46,1146,1147],{"class":48,"line":67},[46,1148,89],{"emptyLinePlaceholder":88},[46,1150,1151],{"class":48,"line":79},[46,1152,1153],{"class":126},"\u002F\u002F Одноаргументная форма — паника если тип не совпал\n",[46,1155,1156,1158,1160,1163,1165],{"class":48,"line":85},[46,1157,146],{"class":63},[46,1159,875],{"class":52},[46,1161,1162],{"class":63}," s.(",[46,1164,149],{"class":56},[46,1166,661],{"class":63},[46,1168,1169,1171,1173,1176],{"class":48,"line":92},[46,1170,217],{"class":63},[46,1172,220],{"class":56},[46,1174,1175],{"class":63},"(u.Name) ",[46,1177,231],{"class":126},[46,1179,1180],{"class":48,"line":105},[46,1181,89],{"emptyLinePlaceholder":88},[46,1183,1184],{"class":48,"line":113},[46,1185,1186],{"class":126},"\u002F\u002F Двухаргументная форма — безопасно\n",[46,1188,1189,1192,1194,1196,1198],{"class":48,"line":118},[46,1190,1191],{"class":63},"u, ok ",[46,1193,875],{"class":52},[46,1195,1162],{"class":63},[46,1197,149],{"class":56},[46,1199,661],{"class":63},[46,1201,1202,1205],{"class":48,"line":123},[46,1203,1204],{"class":52},"if",[46,1206,1207],{"class":63}," ok {\n",[46,1209,1210,1213,1215],{"class":48,"line":130},[46,1211,1212],{"class":63},"    fmt.",[46,1214,220],{"class":56},[46,1216,1217],{"class":63},"(u.Name)\n",[46,1219,1220],{"class":48,"line":136},[46,1221,82],{"class":63},[46,1223,1224],{"class":48,"line":165},[46,1225,89],{"emptyLinePlaceholder":88},[46,1227,1228],{"class":48,"line":174},[46,1229,1230],{"class":126},"\u002F\u002F Частая ошибка: assertion к неправильному типу\n",[46,1232,1233,1235,1237,1239,1241,1243,1245,1247],{"class":48,"line":179},[46,1234,187],{"class":52},[46,1236,190],{"class":63},[46,1238,193],{"class":56},[46,1240,196],{"class":52},[46,1242,97],{"class":56},[46,1244,201],{"class":63},[46,1246,205],{"class":204},[46,1248,82],{"class":63},[46,1250,1251,1254,1256,1258,1260,1262,1264],{"class":48,"line":184},[46,1252,1253],{"class":63},"_, ok ",[46,1255,875],{"class":52},[46,1257,1162],{"class":63},[46,1259,276],{"class":52},[46,1261,149],{"class":56},[46,1263,152],{"class":63},[46,1265,1266],{"class":126},"\u002F\u002F false — s содержит User, не *User\n",[15,1268,1269,1270,1272,1273,1275],{},"Type assertion работает за O(1): Go сравнивает указатель на ",[19,1271,411],{}," в ",[19,1274,496],{}," с ожидаемым — это одно сравнение указателей.",[29,1277],{},[32,1279,1281],{"id":1280},"type-switch","Type switch",[15,1283,1284],{},"Когда нужно обработать несколько возможных типов — используют type switch:",[37,1286,1288],{"className":39,"code":1287,"language":41,"meta":42,"style":42},"func describe(i interface{}) string {\n    switch v := i.(type) {\n    case int:\n        return fmt.Sprintf(\"int: %d\", v)\\n    case string:\n        return fmt.Sprintf(\"string: %q\", v)\\n    case bool:\n        return fmt.Sprintf(\"bool: %v\", v)\\n    case []int:\n        return fmt.Sprintf(\"[]int с длиной %d\", len(v))\\n    case nil:\n        return \"nil\"\n    default:\n        return fmt.Sprintf(\"неизвестный тип: %T\", v)\\n    }\n}\n",[19,1289,1290,1311,1329,1339,1370,1397,1426,1457,1464,1471,1492],{"__ignoreMap":42},[46,1291,1292,1294,1297,1299,1302,1304,1307,1309],{"class":48,"line":49},[46,1293,139],{"class":52},[46,1295,1296],{"class":56}," describe",[46,1298,632],{"class":63},[46,1300,1301],{"class":145},"i",[46,1303,60],{"class":52},[46,1305,1306],{"class":63},"{}) ",[46,1308,160],{"class":52},[46,1310,64],{"class":63},[46,1312,1313,1316,1319,1321,1324,1326],{"class":48,"line":67},[46,1314,1315],{"class":52},"    switch",[46,1317,1318],{"class":63}," v ",[46,1320,875],{"class":52},[46,1322,1323],{"class":63}," i.(",[46,1325,53],{"class":52},[46,1327,1328],{"class":63},") {\n",[46,1330,1331,1334,1336],{"class":48,"line":79},[46,1332,1333],{"class":52},"    case",[46,1335,649],{"class":52},[46,1337,1338],{"class":63},":\n",[46,1340,1341,1343,1346,1349,1351,1354,1357,1360,1363,1366,1368],{"class":48,"line":85},[46,1342,1085],{"class":52},[46,1344,1345],{"class":63}," fmt.",[46,1347,1348],{"class":56},"Sprintf",[46,1350,632],{"class":63},[46,1352,1353],{"class":204},"\"int: ",[46,1355,1356],{"class":284},"%d",[46,1358,1359],{"class":204},"\"",[46,1361,1362],{"class":63},", v)\\n    ",[46,1364,1365],{"class":52},"case",[46,1367,971],{"class":52},[46,1369,1338],{"class":63},[46,1371,1372,1374,1376,1378,1380,1383,1386,1388,1390,1392,1395],{"class":48,"line":92},[46,1373,1085],{"class":52},[46,1375,1345],{"class":63},[46,1377,1348],{"class":56},[46,1379,632],{"class":63},[46,1381,1382],{"class":204},"\"string: ",[46,1384,1385],{"class":284},"%q",[46,1387,1359],{"class":204},[46,1389,1362],{"class":63},[46,1391,1365],{"class":52},[46,1393,1394],{"class":52}," bool",[46,1396,1338],{"class":63},[46,1398,1399,1401,1403,1405,1407,1410,1413,1415,1417,1419,1421,1424],{"class":48,"line":105},[46,1400,1085],{"class":52},[46,1402,1345],{"class":63},[46,1404,1348],{"class":56},[46,1406,632],{"class":63},[46,1408,1409],{"class":204},"\"bool: ",[46,1411,1412],{"class":284},"%v",[46,1414,1359],{"class":204},[46,1416,1362],{"class":63},[46,1418,1365],{"class":52},[46,1420,637],{"class":63},[46,1422,1423],{"class":52},"int",[46,1425,1338],{"class":63},[46,1427,1428,1430,1432,1434,1436,1439,1441,1443,1445,1448,1451,1453,1455],{"class":48,"line":113},[46,1429,1085],{"class":52},[46,1431,1345],{"class":63},[46,1433,1348],{"class":56},[46,1435,632],{"class":63},[46,1437,1438],{"class":204},"\"[]int с длиной ",[46,1440,1356],{"class":284},[46,1442,1359],{"class":204},[46,1444,652],{"class":63},[46,1446,1447],{"class":56},"len",[46,1449,1450],{"class":63},"(v))\\n    ",[46,1452,1365],{"class":52},[46,1454,846],{"class":284},[46,1456,1338],{"class":63},[46,1458,1459,1461],{"class":48,"line":118},[46,1460,1085],{"class":52},[46,1462,1463],{"class":204}," \"nil\"\n",[46,1465,1466,1469],{"class":48,"line":123},[46,1467,1468],{"class":52},"    default",[46,1470,1338],{"class":63},[46,1472,1473,1475,1477,1479,1481,1484,1487,1489],{"class":48,"line":130},[46,1474,1085],{"class":52},[46,1476,1345],{"class":63},[46,1478,1348],{"class":56},[46,1480,632],{"class":63},[46,1482,1483],{"class":204},"\"неизвестный тип: ",[46,1485,1486],{"class":284},"%T",[46,1488,1359],{"class":204},[46,1490,1491],{"class":63},", v)\\n    }\n",[46,1493,1494],{"class":48,"line":136},[46,1495,82],{"class":63},[15,1497,1498,1501],{},[19,1499,1500],{},"v"," в каждой ветке имеет конкретный тип, а не интерфейсный — компилятор это знает и позволяет обращаться к методам без дополнительных assertion.",[29,1503],{},[32,1505,1507],{"id":1506},"интерфейсы-и-производительность","Интерфейсы и производительность",[15,1509,1510],{},"Вызов метода через интерфейс дороже прямого вызова:",[1512,1513,1514,1523,1529,1533],"ol",{},[1515,1516,1517,1518,1520,1521],"li",{},"Загружаем ",[19,1519,411],{}," из ",[19,1522,496],{},[1515,1524,1525,1526],{},"Ищем нужный метод в таблице ",[19,1527,1528],{},"fun",[1515,1530,1517,1531],{},[19,1532,527],{},[1515,1534,1535],{},"Вызываем",[15,1537,1538,1539,1542],{},"Это называется ",[24,1540,1541],{},"dynamic dispatch"," — диспетчеризация в рантайме. Компилятор не может заинлайнить вызов через интерфейс (в большинстве случаев), потому что не знает конкретный тип на этапе компиляции.",[15,1544,1545],{},"На практике это редко узкое место — разница в единицы наносекунд. Но в очень горячих путях (миллионы вызовов в секунду) конкретный тип вместо интерфейса может дать ощутимый прирост.",[37,1547,1549],{"className":39,"code":1548,"language":41,"meta":42,"style":42},"\u002F\u002F Медленнее в hot path: dynamic dispatch\nfunc sumInterface(items []Summable) int {\n    total := 0\n    for _, item := range items {\n        total += item.Sum() \u002F\u002F indirect call через itab\n    }\n    return total\n}\n\n\u002F\u002F Быстрее: конкретный тип, компилятор может инлайнить\nfunc sumConcrete(items []MyType) int {\n    total := 0\n    for _, item := range items {\n        total += item.Sum() \u002F\u002F direct call, возможен инлайнинг\n    }\n    return total\n}\n",[19,1550,1551,1556,1579,1589,1605,1624,1628,1635,1639,1643,1648,1670,1678,1690,1705,1709,1715],{"__ignoreMap":42},[46,1552,1553],{"class":48,"line":49},[46,1554,1555],{"class":126},"\u002F\u002F Медленнее в hot path: dynamic dispatch\n",[46,1557,1558,1560,1563,1565,1568,1570,1573,1575,1577],{"class":48,"line":67},[46,1559,139],{"class":52},[46,1561,1562],{"class":56}," sumInterface",[46,1564,632],{"class":63},[46,1566,1567],{"class":145},"items",[46,1569,637],{"class":63},[46,1571,1572],{"class":56},"Summable",[46,1574,152],{"class":63},[46,1576,1423],{"class":52},[46,1578,64],{"class":63},[46,1580,1581,1584,1586],{"class":48,"line":79},[46,1582,1583],{"class":63},"    total ",[46,1585,875],{"class":52},[46,1587,1588],{"class":284}," 0\n",[46,1590,1591,1594,1597,1599,1602],{"class":48,"line":85},[46,1592,1593],{"class":52},"    for",[46,1595,1596],{"class":63}," _, item ",[46,1598,875],{"class":52},[46,1600,1601],{"class":52}," range",[46,1603,1604],{"class":63}," items {\n",[46,1606,1607,1610,1613,1616,1619,1621],{"class":48,"line":92},[46,1608,1609],{"class":63},"        total ",[46,1611,1612],{"class":52},"+=",[46,1614,1615],{"class":63}," item.",[46,1617,1618],{"class":56},"Sum",[46,1620,73],{"class":63},[46,1622,1623],{"class":126},"\u002F\u002F indirect call через itab\n",[46,1625,1626],{"class":48,"line":105},[46,1627,1028],{"class":63},[46,1629,1630,1632],{"class":48,"line":113},[46,1631,168],{"class":52},[46,1633,1634],{"class":63}," total\n",[46,1636,1637],{"class":48,"line":118},[46,1638,82],{"class":63},[46,1640,1641],{"class":48,"line":123},[46,1642,89],{"emptyLinePlaceholder":88},[46,1644,1645],{"class":48,"line":130},[46,1646,1647],{"class":126},"\u002F\u002F Быстрее: конкретный тип, компилятор может инлайнить\n",[46,1649,1650,1652,1655,1657,1659,1661,1664,1666,1668],{"class":48,"line":136},[46,1651,139],{"class":52},[46,1653,1654],{"class":56}," sumConcrete",[46,1656,632],{"class":63},[46,1658,1567],{"class":145},[46,1660,637],{"class":63},[46,1662,1663],{"class":56},"MyType",[46,1665,152],{"class":63},[46,1667,1423],{"class":52},[46,1669,64],{"class":63},[46,1671,1672,1674,1676],{"class":48,"line":165},[46,1673,1583],{"class":63},[46,1675,875],{"class":52},[46,1677,1588],{"class":284},[46,1679,1680,1682,1684,1686,1688],{"class":48,"line":174},[46,1681,1593],{"class":52},[46,1683,1596],{"class":63},[46,1685,875],{"class":52},[46,1687,1601],{"class":52},[46,1689,1604],{"class":63},[46,1691,1692,1694,1696,1698,1700,1702],{"class":48,"line":179},[46,1693,1609],{"class":63},[46,1695,1612],{"class":52},[46,1697,1615],{"class":63},[46,1699,1618],{"class":56},[46,1701,73],{"class":63},[46,1703,1704],{"class":126},"\u002F\u002F direct call, возможен инлайнинг\n",[46,1706,1707],{"class":48,"line":184},[46,1708,1028],{"class":63},[46,1710,1711,1713],{"class":48,"line":214},[46,1712,168],{"class":52},[46,1714,1634],{"class":63},[46,1716,1718],{"class":48,"line":1717},17,[46,1719,82],{"class":63},[29,1721],{},[32,1723,1725],{"id":1724},"паттерны-работы-с-интерфейсами","Паттерны работы с интерфейсами",[308,1727,1729],{"id":1728},"accept-interfaces-return-structs","Accept interfaces, return structs",[15,1731,1732],{},"Классический Go-принцип: функции принимают интерфейсы (гибкость), возвращают конкретные типы (ясность):",[37,1734,1736],{"className":39,"code":1735,"language":41,"meta":42,"style":42},"\u002F\u002F Хорошо: принимает io.Reader — работает с файлом, сетью, буфером, чем угодно\nfunc ParseConfig(r io.Reader) (*Config, error) {\n    \u002F\u002F ...\n}\n\n\u002F\u002F Плохо: возвращать интерфейс — скрывает реальный тип, затрудняет использование\nfunc NewStorage() Storage { \u002F\u002F интерфейс\n    return &RedisStorage{}\n}\n\n\u002F\u002F Хорошо: возвращать конкретный тип\nfunc NewStorage() *RedisStorage {\n    return &RedisStorage{}\n}\n",[19,1737,1738,1743,1776,1781,1785,1789,1794,1812,1824,1828,1832,1837,1851,1861],{"__ignoreMap":42},[46,1739,1740],{"class":48,"line":49},[46,1741,1742],{"class":126},"\u002F\u002F Хорошо: принимает io.Reader — работает с файлом, сетью, буфером, чем угодно\n",[46,1744,1745,1747,1750,1752,1755,1758,1760,1763,1765,1767,1770,1772,1774],{"class":48,"line":67},[46,1746,139],{"class":52},[46,1748,1749],{"class":56}," ParseConfig",[46,1751,632],{"class":63},[46,1753,1754],{"class":145},"r",[46,1756,1757],{"class":56}," io",[46,1759,359],{"class":63},[46,1761,1762],{"class":56},"Reader",[46,1764,643],{"class":63},[46,1766,276],{"class":52},[46,1768,1769],{"class":56},"Config",[46,1771,652],{"class":63},[46,1773,826],{"class":52},[46,1775,1328],{"class":63},[46,1777,1778],{"class":48,"line":79},[46,1779,1780],{"class":126},"    \u002F\u002F ...\n",[46,1782,1783],{"class":48,"line":85},[46,1784,82],{"class":63},[46,1786,1787],{"class":48,"line":92},[46,1788,89],{"emptyLinePlaceholder":88},[46,1790,1791],{"class":48,"line":105},[46,1792,1793],{"class":126},"\u002F\u002F Плохо: возвращать интерфейс — скрывает реальный тип, затрудняет использование\n",[46,1795,1796,1798,1801,1803,1806,1809],{"class":48,"line":113},[46,1797,139],{"class":52},[46,1799,1800],{"class":56}," NewStorage",[46,1802,73],{"class":63},[46,1804,1805],{"class":56},"Storage",[46,1807,1808],{"class":63}," { ",[46,1810,1811],{"class":126},"\u002F\u002F интерфейс\n",[46,1813,1814,1816,1818,1821],{"class":48,"line":118},[46,1815,168],{"class":52},[46,1817,588],{"class":52},[46,1819,1820],{"class":56},"RedisStorage",[46,1822,1823],{"class":63},"{}\n",[46,1825,1826],{"class":48,"line":123},[46,1827,82],{"class":63},[46,1829,1830],{"class":48,"line":130},[46,1831,89],{"emptyLinePlaceholder":88},[46,1833,1834],{"class":48,"line":136},[46,1835,1836],{"class":126},"\u002F\u002F Хорошо: возвращать конкретный тип\n",[46,1838,1839,1841,1843,1845,1847,1849],{"class":48,"line":165},[46,1840,139],{"class":52},[46,1842,1800],{"class":56},[46,1844,73],{"class":63},[46,1846,276],{"class":52},[46,1848,1820],{"class":56},[46,1850,64],{"class":63},[46,1852,1853,1855,1857,1859],{"class":48,"line":174},[46,1854,168],{"class":52},[46,1856,588],{"class":52},[46,1858,1820],{"class":56},[46,1860,1823],{"class":63},[46,1862,1863],{"class":48,"line":179},[46,1864,82],{"class":63},[15,1866,1867],{},"Исключение: когда возвращаемый интерфейс — часть публичного API и скрытие реализации принципиально (фабричный паттерн, моки).",[308,1869,1871],{"id":1870},"определяй-интерфейс-там-где-используешь","Определяй интерфейс там, где используешь",[15,1873,1874,1875,1878],{},"В Go интерфейс лучше определять на стороне ",[24,1876,1877],{},"потребителя",", а не производителя:",[37,1880,1882],{"className":39,"code":1881,"language":41,"meta":42,"style":42},"\u002F\u002F Пакет storage — экспортирует конкретный тип\npackage storage\n\ntype Postgres struct{}\nfunc (p *Postgres) GetUser(id int) (*User, error) { ... }\nfunc (p *Postgres) SaveUser(u *User) error { ... }\n\n\u002F\u002F Пакет service — определяет интерфейс под свои нужды\npackage service\n\n\u002F\u002F Нужен только GetUser — определяем минимальный интерфейс\ntype UserRepository interface {\n    GetUser(id int) (*User, error)\n}\n\ntype UserService struct {\n    repo UserRepository\n}\n",[19,1883,1884,1889,1897,1901,1912,1957,1994,1998,2003,2010,2014,2019,2030,2053,2057,2061,2072,2080],{"__ignoreMap":42},[46,1885,1886],{"class":48,"line":49},[46,1887,1888],{"class":126},"\u002F\u002F Пакет storage — экспортирует конкретный тип\n",[46,1890,1891,1894],{"class":48,"line":67},[46,1892,1893],{"class":52},"package",[46,1895,1896],{"class":56}," storage\n",[46,1898,1899],{"class":48,"line":79},[46,1900,89],{"emptyLinePlaceholder":88},[46,1902,1903,1905,1908,1910],{"class":48,"line":85},[46,1904,53],{"class":52},[46,1906,1907],{"class":56}," Postgres",[46,1909,100],{"class":52},[46,1911,1823],{"class":63},[46,1913,1914,1916,1918,1921,1923,1926,1928,1931,1933,1936,1938,1940,1942,1944,1946,1948,1951,1954],{"class":48,"line":92},[46,1915,139],{"class":52},[46,1917,142],{"class":63},[46,1919,1920],{"class":145},"p ",[46,1922,276],{"class":52},[46,1924,1925],{"class":56},"Postgres",[46,1927,152],{"class":63},[46,1929,1930],{"class":56},"GetUser",[46,1932,632],{"class":63},[46,1934,1935],{"class":145},"id",[46,1937,649],{"class":52},[46,1939,643],{"class":63},[46,1941,276],{"class":52},[46,1943,149],{"class":56},[46,1945,652],{"class":63},[46,1947,826],{"class":52},[46,1949,1950],{"class":63},") { ",[46,1952,1953],{"class":52},"...",[46,1955,1956],{"class":63}," }\n",[46,1958,1959,1961,1963,1965,1967,1969,1971,1974,1976,1979,1982,1984,1986,1988,1990,1992],{"class":48,"line":105},[46,1960,139],{"class":52},[46,1962,142],{"class":63},[46,1964,1920],{"class":145},[46,1966,276],{"class":52},[46,1968,1925],{"class":56},[46,1970,152],{"class":63},[46,1972,1973],{"class":56},"SaveUser",[46,1975,632],{"class":63},[46,1977,1978],{"class":145},"u",[46,1980,1981],{"class":52}," *",[46,1983,149],{"class":56},[46,1985,152],{"class":63},[46,1987,826],{"class":52},[46,1989,1808],{"class":63},[46,1991,1953],{"class":52},[46,1993,1956],{"class":63},[46,1995,1996],{"class":48,"line":113},[46,1997,89],{"emptyLinePlaceholder":88},[46,1999,2000],{"class":48,"line":118},[46,2001,2002],{"class":126},"\u002F\u002F Пакет service — определяет интерфейс под свои нужды\n",[46,2004,2005,2007],{"class":48,"line":123},[46,2006,1893],{"class":52},[46,2008,2009],{"class":56}," service\n",[46,2011,2012],{"class":48,"line":130},[46,2013,89],{"emptyLinePlaceholder":88},[46,2015,2016],{"class":48,"line":136},[46,2017,2018],{"class":126},"\u002F\u002F Нужен только GetUser — определяем минимальный интерфейс\n",[46,2020,2021,2023,2026,2028],{"class":48,"line":165},[46,2022,53],{"class":52},[46,2024,2025],{"class":56}," UserRepository",[46,2027,60],{"class":52},[46,2029,64],{"class":63},[46,2031,2032,2035,2037,2039,2041,2043,2045,2047,2049,2051],{"class":48,"line":174},[46,2033,2034],{"class":56},"    GetUser",[46,2036,632],{"class":63},[46,2038,1935],{"class":145},[46,2040,649],{"class":52},[46,2042,643],{"class":63},[46,2044,276],{"class":52},[46,2046,149],{"class":56},[46,2048,652],{"class":63},[46,2050,826],{"class":52},[46,2052,661],{"class":63},[46,2054,2055],{"class":48,"line":179},[46,2056,82],{"class":63},[46,2058,2059],{"class":48,"line":184},[46,2060,89],{"emptyLinePlaceholder":88},[46,2062,2063,2065,2068,2070],{"class":48,"line":214},[46,2064,53],{"class":52},[46,2066,2067],{"class":56}," UserService",[46,2069,100],{"class":52},[46,2071,64],{"class":63},[46,2073,2074,2077],{"class":48,"line":1717},[46,2075,2076],{"class":63},"    repo ",[46,2078,2079],{"class":56},"UserRepository\n",[46,2081,2083],{"class":48,"line":2082},18,[46,2084,82],{"class":63},[15,2086,2087],{},"Это делает зависимости явными, а тестирование — тривиальным: достаточно написать mock с одним методом.",[308,2089,2091],{"id":2090},"интерфейсы-для-тестирования","Интерфейсы для тестирования",[37,2093,2095],{"className":39,"code":2094,"language":41,"meta":42,"style":42},"\u002F\u002F Продакшн-код зависит от интерфейса\ntype Mailer interface {\n    Send(to, subject, body string) error\n}\n\ntype OrderService struct {\n    mailer Mailer\n}\n\n\u002F\u002F Мок для тестов — реализует тот же интерфейс\ntype MockMailer struct {\n    Sent []string\n}\n\nfunc (m *MockMailer) Send(to, subject, body string) error {\n    m.Sent = append(m.Sent, to)\n    return nil\n}\n\nfunc TestOrderService(t *testing.T) {\n    mock := &MockMailer{}\n    svc := OrderService{mailer: mock}\n    svc.PlaceOrder(...)\n    assert.Contains(t, mock.Sent, \"customer@example.com\")\n}\n",[19,2096,2097,2102,2113,2140,2144,2148,2159,2167,2171,2175,2180,2191,2198,2202,2206,2245,2258,2265,2269,2274,2299,2313,2326,2341,2358],{"__ignoreMap":42},[46,2098,2099],{"class":48,"line":49},[46,2100,2101],{"class":126},"\u002F\u002F Продакшн-код зависит от интерфейса\n",[46,2103,2104,2106,2109,2111],{"class":48,"line":67},[46,2105,53],{"class":52},[46,2107,2108],{"class":56}," Mailer",[46,2110,60],{"class":52},[46,2112,64],{"class":63},[46,2114,2115,2118,2120,2123,2125,2128,2130,2133,2135,2137],{"class":48,"line":79},[46,2116,2117],{"class":56},"    Send",[46,2119,632],{"class":63},[46,2121,2122],{"class":145},"to",[46,2124,652],{"class":63},[46,2126,2127],{"class":145},"subject",[46,2129,652],{"class":63},[46,2131,2132],{"class":145},"body",[46,2134,971],{"class":52},[46,2136,152],{"class":63},[46,2138,2139],{"class":52},"error\n",[46,2141,2142],{"class":48,"line":85},[46,2143,82],{"class":63},[46,2145,2146],{"class":48,"line":92},[46,2147,89],{"emptyLinePlaceholder":88},[46,2149,2150,2152,2155,2157],{"class":48,"line":105},[46,2151,53],{"class":52},[46,2153,2154],{"class":56}," OrderService",[46,2156,100],{"class":52},[46,2158,64],{"class":63},[46,2160,2161,2164],{"class":48,"line":113},[46,2162,2163],{"class":63},"    mailer ",[46,2165,2166],{"class":56},"Mailer\n",[46,2168,2169],{"class":48,"line":118},[46,2170,82],{"class":63},[46,2172,2173],{"class":48,"line":123},[46,2174,89],{"emptyLinePlaceholder":88},[46,2176,2177],{"class":48,"line":130},[46,2178,2179],{"class":126},"\u002F\u002F Мок для тестов — реализует тот же интерфейс\n",[46,2181,2182,2184,2187,2189],{"class":48,"line":136},[46,2183,53],{"class":52},[46,2185,2186],{"class":56}," MockMailer",[46,2188,100],{"class":52},[46,2190,64],{"class":63},[46,2192,2193,2196],{"class":48,"line":165},[46,2194,2195],{"class":63},"    Sent []",[46,2197,76],{"class":52},[46,2199,2200],{"class":48,"line":174},[46,2201,82],{"class":63},[46,2203,2204],{"class":48,"line":179},[46,2205,89],{"emptyLinePlaceholder":88},[46,2207,2208,2210,2212,2215,2217,2220,2222,2225,2227,2229,2231,2233,2235,2237,2239,2241,2243],{"class":48,"line":184},[46,2209,139],{"class":52},[46,2211,142],{"class":63},[46,2213,2214],{"class":145},"m ",[46,2216,276],{"class":52},[46,2218,2219],{"class":56},"MockMailer",[46,2221,152],{"class":63},[46,2223,2224],{"class":56},"Send",[46,2226,632],{"class":63},[46,2228,2122],{"class":145},[46,2230,652],{"class":63},[46,2232,2127],{"class":145},[46,2234,652],{"class":63},[46,2236,2132],{"class":145},[46,2238,971],{"class":52},[46,2240,152],{"class":63},[46,2242,826],{"class":52},[46,2244,64],{"class":63},[46,2246,2247,2250,2252,2255],{"class":48,"line":214},[46,2248,2249],{"class":63},"    m.Sent ",[46,2251,562],{"class":52},[46,2253,2254],{"class":56}," append",[46,2256,2257],{"class":63},"(m.Sent, to)\n",[46,2259,2260,2262],{"class":48,"line":1717},[46,2261,168],{"class":52},[46,2263,2264],{"class":284}," nil\n",[46,2266,2267],{"class":48,"line":2082},[46,2268,82],{"class":63},[46,2270,2272],{"class":48,"line":2271},19,[46,2273,89],{"emptyLinePlaceholder":88},[46,2275,2277,2279,2282,2284,2287,2289,2292,2294,2297],{"class":48,"line":2276},20,[46,2278,139],{"class":52},[46,2280,2281],{"class":56}," TestOrderService",[46,2283,632],{"class":63},[46,2285,2286],{"class":145},"t",[46,2288,1981],{"class":52},[46,2290,2291],{"class":56},"testing",[46,2293,359],{"class":63},[46,2295,2296],{"class":56},"T",[46,2298,1328],{"class":63},[46,2300,2302,2305,2307,2309,2311],{"class":48,"line":2301},21,[46,2303,2304],{"class":63},"    mock ",[46,2306,875],{"class":52},[46,2308,588],{"class":52},[46,2310,2219],{"class":56},[46,2312,1823],{"class":63},[46,2314,2316,2319,2321,2323],{"class":48,"line":2315},22,[46,2317,2318],{"class":63},"    svc ",[46,2320,875],{"class":52},[46,2322,2154],{"class":56},[46,2324,2325],{"class":63},"{mailer: mock}\n",[46,2327,2329,2332,2335,2337,2339],{"class":48,"line":2328},23,[46,2330,2331],{"class":63},"    svc.",[46,2333,2334],{"class":56},"PlaceOrder",[46,2336,632],{"class":63},[46,2338,1953],{"class":52},[46,2340,661],{"class":63},[46,2342,2344,2347,2350,2353,2356],{"class":48,"line":2343},24,[46,2345,2346],{"class":63},"    assert.",[46,2348,2349],{"class":56},"Contains",[46,2351,2352],{"class":63},"(t, mock.Sent, ",[46,2354,2355],{"class":204},"\"customer@example.com\"",[46,2357,661],{"class":63},[46,2359,2361],{"class":48,"line":2360},25,[46,2362,82],{"class":63},[29,2364],{},[32,2366,2368],{"id":2367},"вопросы-на-собеседовании","Вопросы на собеседовании",[15,2370,2371,2374,2377,2378,2380],{},[24,2372,2373],{},"Q: Как Go определяет, что тип реализует интерфейс?",[2375,2376],"br",{},"\nA: Structural typing — компилятор проверяет наличие всех методов интерфейса с совпадающими сигнатурами. Явного объявления (",[19,2379,21],{},") не нужно. Проверка происходит в момент присвоения значения переменной интерфейсного типа.",[15,2382,2383,2386,2388,2389,2391,2392,2394,2395,779,2397,2399,2400,2402],{},[24,2384,2385],{},"Q: Из чего состоит интерфейсная переменная внутри?",[2375,2387],{},"\nA: Из двух машинных слов. Для непустого интерфейса — ",[19,2390,496],{},": указатель на ",[19,2393,411],{}," (тип + таблица методов) и указатель на данные. Для пустого ",[19,2396,314],{},[19,2398,318],{}," — ",[19,2401,374],{},": указатель на тип и указатель на данные, без таблицы методов.",[15,2404,2405,2414,2416,2417,2419,2420,2422,2423,2425],{},[24,2406,2407,2408,2410,2411,2413],{},"Q: Чем ",[19,2409,496],{}," отличается от ",[19,2412,374],{},"?",[2375,2415],{},"\nA: ",[19,2418,374],{}," используется для пустого интерфейса — хранит только тип и данные, таблицы методов нет. ",[19,2421,496],{}," — для непустых интерфейсов — вместо голого типа хранит ",[19,2424,411],{},": структуру с типом и таблицей указателей на методы для диспетчеризации вызовов.",[15,2427,2428,2431,2433,2434,2437,2438,2440],{},[24,2429,2430],{},"Q: Когда интерфейс равен nil?",[2375,2432],{},"\nA: Только когда оба поля — тип и данные — равны nil. Если тип задан, а данные nil (typed nil pointer, упакованный в интерфейс) — интерфейс не nil. Это главная ловушка: ",[19,2435,2436],{},"var p *MyError = nil; return p"," при возврате через ",[19,2439,826],{}," даст ненулевой интерфейс.",[15,2442,2443,2446,2448,2449,2452,2453,2456,2457,2460],{},[24,2444,2445],{},"Q: Что безопаснее — одноаргументный или двухаргументный type assertion?",[2375,2447],{},"\nA: Двухаргументный: ",[19,2450,2451],{},"v, ok := i.(Type)",". Одноаргументный ",[19,2454,2455],{},"v := i.(Type)"," паникует, если тип не совпал. В продакшн-коде почти всегда нужна форма с ",[19,2458,2459],{},"ok",", кроме случаев, когда несоответствие типа — программная ошибка, а не ожидаемая ситуация.",[15,2462,2463,2466,2468,2469,2471,2472,2474],{},[24,2464,2465],{},"Q: В чём разница между type assertion и type switch?",[2375,2467],{},"\nA: Type assertion проверяет один конкретный тип: ",[19,2470,2451],{},". Type switch обрабатывает несколько вариантов последовательно, и в каждой ветке ",[19,2473,1500],{}," имеет конкретный тип — компилятор это знает. Type switch предпочтительнее цепочки assertion'ов.",[15,2476,2477,2480,2482,2483,2485,2486,2488],{},[24,2478,2479],{},"Q: Почему вызов метода через интерфейс медленнее прямого вызова?",[2375,2481],{},"\nA: Dynamic dispatch: в рантайме нужно загрузить ",[19,2484,411],{},", найти адрес метода в таблице ",[19,2487,1528],{},", потом вызвать. Прямой вызов известен на этапе компиляции и может быть заинлайнен. Разница — единицы наносекунд, критично только в очень горячих путях.",[15,2490,2491,2494,2496],{},[24,2492,2493],{},"Q: Что означает принцип \"accept interfaces, return structs\"?",[2375,2495],{},"\nA: Функции принимают интерфейсы — это даёт гибкость: любой тип с нужными методами подходит. Возвращают конкретные типы — это даёт ясность: вызывающий видит реальную структуру и имеет доступ ко всем её методам, а не только к тем, что объявлены в интерфейсе.",[15,2498,2499,2502,2504],{},[24,2500,2501],{},"Q: Где лучше определять интерфейс — в пакете производителя или потребителя?",[2375,2503],{},"\nA: На стороне потребителя. Это позволяет определить минимальный интерфейс под конкретные нужды, не завися от того, что экспортирует производитель. Производитель возвращает конкретный тип, а потребитель сам решает, какую абстракцию ему нужна.",[15,2506,2507,2510,2416,2512,2515,2516,2518,2519,2522,2523,2526],{},[24,2508,2509],{},"Q: Как во время компиляции проверить, что тип реализует интерфейс?",[2375,2511],{},[19,2513,2514],{},"var _ MyInterface = (*MyType)(nil)"," — присвоение nil-указателя нужного типа переменной интерфейсного типа. Если ",[19,2517,1663],{}," не реализует ",[19,2520,2521],{},"MyInterface"," — ошибка компиляции. ",[19,2524,2525],{},"_"," означает, что переменная нам не нужна, это чисто compile-time проверка.",[29,2528],{},[10,2530,2532],{"id":2531},"задачи-интерфейс","Задачи: Интерфейс",[29,2534],{},[15,2536,2537],{},[24,2538,2539],{},"Задача 1: Реализация интерфейса",[15,2541,2542,2545],{},[24,2543,2544],{},"Уровень:"," Лёгкая",[15,2547,2548,2551],{},[24,2549,2550],{},"Что проверяет:"," базовое понимание интерфейсов и duck typing",[15,2553,2554,2557,2558,2561,2562,796,2565,2568,2569,2571,2572,2575],{},[24,2555,2556],{},"Условие:"," Дан интерфейс ",[19,2559,2560],{},"Shape",". Реализуй два типа ",[19,2563,2564],{},"Circle",[19,2566,2567],{},"Rectangle",", каждый реализует ",[19,2570,2560],{},". Напиши функцию ",[19,2573,2574],{},"totalArea(shapes []Shape) float64"," которая возвращает суммарную площадь.",[37,2577,2579],{"className":39,"code":2578,"language":41,"meta":42,"style":42},"type Shape interface {\n    Area() float64\n    Perimeter() float64\n}\n",[19,2580,2581,2592,2602,2611],{"__ignoreMap":42},[46,2582,2583,2585,2588,2590],{"class":48,"line":49},[46,2584,53],{"class":52},[46,2586,2587],{"class":56}," Shape",[46,2589,60],{"class":52},[46,2591,64],{"class":63},[46,2593,2594,2597,2599],{"class":48,"line":67},[46,2595,2596],{"class":56},"    Area",[46,2598,73],{"class":63},[46,2600,2601],{"class":52},"float64\n",[46,2603,2604,2607,2609],{"class":48,"line":79},[46,2605,2606],{"class":56},"    Perimeter",[46,2608,73],{"class":63},[46,2610,2601],{"class":52},[46,2612,2613],{"class":48,"line":85},[46,2614,82],{"class":63},[15,2616,2617],{},[24,2618,2619],{},"Примеры:",[37,2621,2626],{"className":2622,"code":2624,"language":2625},[2623],"language-text","Circle{Radius: 5}       → Area ≈ 78.54, Perimeter ≈ 31.42\nRectangle{Width: 4, Height: 6} → Area = 24, Perimeter = 20\ntotalArea([]Shape{Circle{5}, Rectangle{4, 6}}) ≈ 102.54\n","text",[19,2627,2624],{"__ignoreMap":42},[15,2629,2630],{},[24,2631,2632],{},"Решение:",[37,2634,2636],{"className":39,"code":2635,"language":41,"meta":42,"style":42},"package main\n\nimport (\n    \"fmt\"\n    \"math\"\n)\n\ntype Shape interface {\n    Area() float64\n    Perimeter() float64\n}\n\ntype Circle struct{ Radius float64 }\ntype Rectangle struct{ Width, Height float64 }\n\nfunc (c Circle) Area() float64      { return math.Pi * c.Radius * c.Radius }\nfunc (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }\n\nfunc (r Rectangle) Area() float64      { return r.Width * r.Height }\nfunc (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) }\n\nfunc totalArea(shapes []Shape) float64 {\n    total := 0.0\n    for _, s := range shapes {\n        total += s.Area()\n    }\n    return total\n}\n\nfunc main() {\n    shapes := []Shape{Circle{Radius: 5}, Rectangle{Width: 4, Height: 6}}\n    fmt.Printf(\"%.2f\\n\", totalArea(shapes)) \u002F\u002F 102.54\n}\n",[19,2637,2638,2645,2649,2657,2668,2677,2681,2685,2695,2703,2711,2715,2719,2736,2752,2756,2795,2829,2833,2864,2899,2903,2925,2934,2948,2961,2966,2973,2978,2983,2994,3036,3064],{"__ignoreMap":42},[46,2639,2640,2642],{"class":48,"line":49},[46,2641,1893],{"class":52},[46,2643,2644],{"class":56}," main\n",[46,2646,2647],{"class":48,"line":67},[46,2648,89],{"emptyLinePlaceholder":88},[46,2650,2651,2654],{"class":48,"line":79},[46,2652,2653],{"class":52},"import",[46,2655,2656],{"class":63}," (\n",[46,2658,2659,2662,2665],{"class":48,"line":85},[46,2660,2661],{"class":204},"    \"",[46,2663,2664],{"class":56},"fmt",[46,2666,2667],{"class":204},"\"\n",[46,2669,2670,2672,2675],{"class":48,"line":92},[46,2671,2661],{"class":204},[46,2673,2674],{"class":56},"math",[46,2676,2667],{"class":204},[46,2678,2679],{"class":48,"line":105},[46,2680,661],{"class":63},[46,2682,2683],{"class":48,"line":113},[46,2684,89],{"emptyLinePlaceholder":88},[46,2686,2687,2689,2691,2693],{"class":48,"line":118},[46,2688,53],{"class":52},[46,2690,2587],{"class":56},[46,2692,60],{"class":52},[46,2694,64],{"class":63},[46,2696,2697,2699,2701],{"class":48,"line":123},[46,2698,2596],{"class":56},[46,2700,73],{"class":63},[46,2702,2601],{"class":52},[46,2704,2705,2707,2709],{"class":48,"line":130},[46,2706,2606],{"class":56},[46,2708,73],{"class":63},[46,2710,2601],{"class":52},[46,2712,2713],{"class":48,"line":136},[46,2714,82],{"class":63},[46,2716,2717],{"class":48,"line":165},[46,2718,89],{"emptyLinePlaceholder":88},[46,2720,2721,2723,2726,2728,2731,2734],{"class":48,"line":174},[46,2722,53],{"class":52},[46,2724,2725],{"class":56}," Circle",[46,2727,100],{"class":52},[46,2729,2730],{"class":63},"{ Radius ",[46,2732,2733],{"class":52},"float64",[46,2735,1956],{"class":63},[46,2737,2738,2740,2743,2745,2748,2750],{"class":48,"line":179},[46,2739,53],{"class":52},[46,2741,2742],{"class":56}," Rectangle",[46,2744,100],{"class":52},[46,2746,2747],{"class":63},"{ Width, Height ",[46,2749,2733],{"class":52},[46,2751,1956],{"class":63},[46,2753,2754],{"class":48,"line":184},[46,2755,89],{"emptyLinePlaceholder":88},[46,2757,2758,2760,2762,2765,2767,2769,2772,2774,2776,2779,2782,2785,2787,2790,2792],{"class":48,"line":214},[46,2759,139],{"class":52},[46,2761,142],{"class":63},[46,2763,2764],{"class":145},"c ",[46,2766,2564],{"class":56},[46,2768,152],{"class":63},[46,2770,2771],{"class":56},"Area",[46,2773,73],{"class":63},[46,2775,2733],{"class":52},[46,2777,2778],{"class":63},"      { ",[46,2780,2781],{"class":52},"return",[46,2783,2784],{"class":63}," math.Pi ",[46,2786,276],{"class":52},[46,2788,2789],{"class":63}," c.Radius ",[46,2791,276],{"class":52},[46,2793,2794],{"class":63}," c.Radius }\n",[46,2796,2797,2799,2801,2803,2805,2807,2810,2812,2814,2816,2818,2821,2823,2825,2827],{"class":48,"line":1717},[46,2798,139],{"class":52},[46,2800,142],{"class":63},[46,2802,2764],{"class":145},[46,2804,2564],{"class":56},[46,2806,152],{"class":63},[46,2808,2809],{"class":56},"Perimeter",[46,2811,73],{"class":63},[46,2813,2733],{"class":52},[46,2815,1808],{"class":63},[46,2817,2781],{"class":52},[46,2819,2820],{"class":284}," 2",[46,2822,1981],{"class":52},[46,2824,2784],{"class":63},[46,2826,276],{"class":52},[46,2828,2794],{"class":63},[46,2830,2831],{"class":48,"line":2082},[46,2832,89],{"emptyLinePlaceholder":88},[46,2834,2835,2837,2839,2842,2844,2846,2848,2850,2852,2854,2856,2859,2861],{"class":48,"line":2271},[46,2836,139],{"class":52},[46,2838,142],{"class":63},[46,2840,2841],{"class":145},"r ",[46,2843,2567],{"class":56},[46,2845,152],{"class":63},[46,2847,2771],{"class":56},[46,2849,73],{"class":63},[46,2851,2733],{"class":52},[46,2853,2778],{"class":63},[46,2855,2781],{"class":52},[46,2857,2858],{"class":63}," r.Width ",[46,2860,276],{"class":52},[46,2862,2863],{"class":63}," r.Height }\n",[46,2865,2866,2868,2870,2872,2874,2876,2878,2880,2882,2884,2886,2888,2890,2893,2896],{"class":48,"line":2276},[46,2867,139],{"class":52},[46,2869,142],{"class":63},[46,2871,2841],{"class":145},[46,2873,2567],{"class":56},[46,2875,152],{"class":63},[46,2877,2809],{"class":56},[46,2879,73],{"class":63},[46,2881,2733],{"class":52},[46,2883,1808],{"class":63},[46,2885,2781],{"class":52},[46,2887,2820],{"class":284},[46,2889,1981],{"class":52},[46,2891,2892],{"class":63}," (r.Width ",[46,2894,2895],{"class":52},"+",[46,2897,2898],{"class":63}," r.Height) }\n",[46,2900,2901],{"class":48,"line":2301},[46,2902,89],{"emptyLinePlaceholder":88},[46,2904,2905,2907,2910,2912,2915,2917,2919,2921,2923],{"class":48,"line":2315},[46,2906,139],{"class":52},[46,2908,2909],{"class":56}," totalArea",[46,2911,632],{"class":63},[46,2913,2914],{"class":145},"shapes",[46,2916,637],{"class":63},[46,2918,2560],{"class":56},[46,2920,152],{"class":63},[46,2922,2733],{"class":52},[46,2924,64],{"class":63},[46,2926,2927,2929,2931],{"class":48,"line":2328},[46,2928,1583],{"class":63},[46,2930,875],{"class":52},[46,2932,2933],{"class":284}," 0.0\n",[46,2935,2936,2938,2941,2943,2945],{"class":48,"line":2343},[46,2937,1593],{"class":52},[46,2939,2940],{"class":63}," _, s ",[46,2942,875],{"class":52},[46,2944,1601],{"class":52},[46,2946,2947],{"class":63}," shapes {\n",[46,2949,2950,2952,2954,2957,2959],{"class":48,"line":2360},[46,2951,1609],{"class":63},[46,2953,1612],{"class":52},[46,2955,2956],{"class":63}," s.",[46,2958,2771],{"class":56},[46,2960,880],{"class":63},[46,2962,2964],{"class":48,"line":2963},26,[46,2965,1028],{"class":63},[46,2967,2969,2971],{"class":48,"line":2968},27,[46,2970,168],{"class":52},[46,2972,1634],{"class":63},[46,2974,2976],{"class":48,"line":2975},28,[46,2977,82],{"class":63},[46,2979,2981],{"class":48,"line":2980},29,[46,2982,89],{"emptyLinePlaceholder":88},[46,2984,2986,2988,2991],{"class":48,"line":2985},30,[46,2987,139],{"class":52},[46,2989,2990],{"class":56}," main",[46,2992,2993],{"class":63},"() {\n",[46,2995,2997,3000,3002,3004,3006,3008,3010,3013,3016,3019,3021,3024,3027,3030,3033],{"class":48,"line":2996},31,[46,2998,2999],{"class":63},"    shapes ",[46,3001,875],{"class":52},[46,3003,637],{"class":63},[46,3005,2560],{"class":56},[46,3007,1018],{"class":63},[46,3009,2564],{"class":56},[46,3011,3012],{"class":63},"{Radius: ",[46,3014,3015],{"class":284},"5",[46,3017,3018],{"class":63},"}, ",[46,3020,2567],{"class":56},[46,3022,3023],{"class":63},"{Width: ",[46,3025,3026],{"class":284},"4",[46,3028,3029],{"class":63},", Height: ",[46,3031,3032],{"class":284},"6",[46,3034,3035],{"class":63},"}}\n",[46,3037,3039,3041,3044,3046,3048,3051,3053,3055,3058,3061],{"class":48,"line":3038},32,[46,3040,1212],{"class":63},[46,3042,3043],{"class":56},"Printf",[46,3045,632],{"class":63},[46,3047,1359],{"class":204},[46,3049,3050],{"class":284},"%.2f\\n",[46,3052,1359],{"class":204},[46,3054,652],{"class":63},[46,3056,3057],{"class":56},"totalArea",[46,3059,3060],{"class":63},"(shapes)) ",[46,3062,3063],{"class":126},"\u002F\u002F 102.54\n",[46,3065,3067],{"class":48,"line":3066},33,[46,3068,82],{"class":63},[29,3070],{},[15,3072,3073],{},[24,3074,3075],{},"Задача 2: Ловушка nil interface",[15,3077,3078,3080],{},[24,3079,2544],{}," Средняя",[15,3082,3083,3085],{},[24,3084,2550],{}," понимание iface, typed nil, главная ловушка интерфейсов",[15,3087,3088,3090],{},[24,3089,2556],{}," Что выведет код? Объясни каждый результат.",[37,3092,3094],{"className":39,"code":3093,"language":41,"meta":42,"style":42},"package main\n\nimport \"fmt\"\n\ntype MyError struct{ msg string }\n\nfunc (e *MyError) Error() string { return e.msg }\n\nfunc getError(fail bool) error {\n    var err *MyError\n    if fail {\n        err = &MyError{\"something went wrong\"}\n    }\n    return err\n}\n\nfunc getErrorFixed(fail bool) error {\n    if fail {\n        return &MyError{\"something went wrong\"}\n    }\n    return nil\n}\n\nfunc main() {\n    err1 := getError(false)\n    err2 := getError(true)\n    err3 := getErrorFixed(false)\n\n    fmt.Println(err1 == nil)\n    fmt.Println(err2 == nil)\n    fmt.Println(err3 == nil)\n    fmt.Println(err1)\n}\n",[19,3095,3096,3102,3106,3117,3121,3137,3141,3170,3174,3193,3204,3211,3228,3232,3239,3243,3247,3266,3272,3286,3290,3296,3300,3304,3312,3328,3344,3359,3363,3378,3393,3408,3417],{"__ignoreMap":42},[46,3097,3098,3100],{"class":48,"line":49},[46,3099,1893],{"class":52},[46,3101,2644],{"class":56},[46,3103,3104],{"class":48,"line":67},[46,3105,89],{"emptyLinePlaceholder":88},[46,3107,3108,3110,3113,3115],{"class":48,"line":79},[46,3109,2653],{"class":52},[46,3111,3112],{"class":204}," \"",[46,3114,2664],{"class":56},[46,3116,2667],{"class":204},[46,3118,3119],{"class":48,"line":85},[46,3120,89],{"emptyLinePlaceholder":88},[46,3122,3123,3125,3128,3130,3133,3135],{"class":48,"line":92},[46,3124,53],{"class":52},[46,3126,3127],{"class":56}," MyError",[46,3129,100],{"class":52},[46,3131,3132],{"class":63},"{ msg ",[46,3134,160],{"class":52},[46,3136,1956],{"class":63},[46,3138,3139],{"class":48,"line":105},[46,3140,89],{"emptyLinePlaceholder":88},[46,3142,3143,3145,3147,3150,3152,3154,3156,3159,3161,3163,3165,3167],{"class":48,"line":113},[46,3144,139],{"class":52},[46,3146,142],{"class":63},[46,3148,3149],{"class":145},"e ",[46,3151,276],{"class":52},[46,3153,841],{"class":56},[46,3155,152],{"class":63},[46,3157,3158],{"class":56},"Error",[46,3160,73],{"class":63},[46,3162,160],{"class":52},[46,3164,1808],{"class":63},[46,3166,2781],{"class":52},[46,3168,3169],{"class":63}," e.msg }\n",[46,3171,3172],{"class":48,"line":118},[46,3173,89],{"emptyLinePlaceholder":88},[46,3175,3176,3178,3180,3182,3185,3187,3189,3191],{"class":48,"line":123},[46,3177,139],{"class":52},[46,3179,821],{"class":56},[46,3181,632],{"class":63},[46,3183,3184],{"class":145},"fail",[46,3186,1394],{"class":52},[46,3188,152],{"class":63},[46,3190,826],{"class":52},[46,3192,64],{"class":63},[46,3194,3195,3197,3199,3201],{"class":48,"line":130},[46,3196,833],{"class":52},[46,3198,984],{"class":63},[46,3200,276],{"class":52},[46,3202,3203],{"class":56},"MyError\n",[46,3205,3206,3208],{"class":48,"line":136},[46,3207,994],{"class":52},[46,3209,3210],{"class":63}," fail {\n",[46,3212,3213,3215,3217,3219,3221,3223,3226],{"class":48,"line":165},[46,3214,1008],{"class":63},[46,3216,562],{"class":52},[46,3218,588],{"class":52},[46,3220,841],{"class":56},[46,3222,1018],{"class":63},[46,3224,3225],{"class":204},"\"something went wrong\"",[46,3227,82],{"class":63},[46,3229,3230],{"class":48,"line":174},[46,3231,1028],{"class":63},[46,3233,3234,3236],{"class":48,"line":179},[46,3235,168],{"class":52},[46,3237,3238],{"class":63}," err\n",[46,3240,3241],{"class":48,"line":184},[46,3242,82],{"class":63},[46,3244,3245],{"class":48,"line":214},[46,3246,89],{"emptyLinePlaceholder":88},[46,3248,3249,3251,3254,3256,3258,3260,3262,3264],{"class":48,"line":1717},[46,3250,139],{"class":52},[46,3252,3253],{"class":56}," getErrorFixed",[46,3255,632],{"class":63},[46,3257,3184],{"class":145},[46,3259,1394],{"class":52},[46,3261,152],{"class":63},[46,3263,826],{"class":52},[46,3265,64],{"class":63},[46,3267,3268,3270],{"class":48,"line":2082},[46,3269,994],{"class":52},[46,3271,3210],{"class":63},[46,3273,3274,3276,3278,3280,3282,3284],{"class":48,"line":2271},[46,3275,1085],{"class":52},[46,3277,588],{"class":52},[46,3279,841],{"class":56},[46,3281,1018],{"class":63},[46,3283,3225],{"class":204},[46,3285,82],{"class":63},[46,3287,3288],{"class":48,"line":2276},[46,3289,1028],{"class":63},[46,3291,3292,3294],{"class":48,"line":2301},[46,3293,168],{"class":52},[46,3295,2264],{"class":284},[46,3297,3298],{"class":48,"line":2315},[46,3299,82],{"class":63},[46,3301,3302],{"class":48,"line":2328},[46,3303,89],{"emptyLinePlaceholder":88},[46,3305,3306,3308,3310],{"class":48,"line":2343},[46,3307,139],{"class":52},[46,3309,2990],{"class":56},[46,3311,2993],{"class":63},[46,3313,3314,3317,3319,3321,3323,3326],{"class":48,"line":2360},[46,3315,3316],{"class":63},"    err1 ",[46,3318,875],{"class":52},[46,3320,821],{"class":56},[46,3322,632],{"class":63},[46,3324,3325],{"class":284},"false",[46,3327,661],{"class":63},[46,3329,3330,3333,3335,3337,3339,3342],{"class":48,"line":2963},[46,3331,3332],{"class":63},"    err2 ",[46,3334,875],{"class":52},[46,3336,821],{"class":56},[46,3338,632],{"class":63},[46,3340,3341],{"class":284},"true",[46,3343,661],{"class":63},[46,3345,3346,3349,3351,3353,3355,3357],{"class":48,"line":2968},[46,3347,3348],{"class":63},"    err3 ",[46,3350,875],{"class":52},[46,3352,3253],{"class":56},[46,3354,632],{"class":63},[46,3356,3325],{"class":284},[46,3358,661],{"class":63},[46,3360,3361],{"class":48,"line":2975},[46,3362,89],{"emptyLinePlaceholder":88},[46,3364,3365,3367,3369,3372,3374,3376],{"class":48,"line":2980},[46,3366,1212],{"class":63},[46,3368,220],{"class":56},[46,3370,3371],{"class":63},"(err1 ",[46,3373,892],{"class":52},[46,3375,846],{"class":284},[46,3377,661],{"class":63},[46,3379,3380,3382,3384,3387,3389,3391],{"class":48,"line":2985},[46,3381,1212],{"class":63},[46,3383,220],{"class":56},[46,3385,3386],{"class":63},"(err2 ",[46,3388,892],{"class":52},[46,3390,846],{"class":284},[46,3392,661],{"class":63},[46,3394,3395,3397,3399,3402,3404,3406],{"class":48,"line":2996},[46,3396,1212],{"class":63},[46,3398,220],{"class":56},[46,3400,3401],{"class":63},"(err3 ",[46,3403,892],{"class":52},[46,3405,846],{"class":284},[46,3407,661],{"class":63},[46,3409,3410,3412,3414],{"class":48,"line":3038},[46,3411,1212],{"class":63},[46,3413,220],{"class":56},[46,3415,3416],{"class":63},"(err1)\n",[46,3418,3419],{"class":48,"line":3066},[46,3420,82],{"class":63},[15,3422,3423],{},[24,3424,3425],{},"Ожидаемый ответ:",[37,3427,3430],{"className":3428,"code":3429,"language":2625},[2623],"false\nfalse\ntrue\n\u003Cnil>\n",[19,3431,3429],{"__ignoreMap":42},[15,3433,3434],{},[24,3435,2632],{},[37,3437,3439],{"className":39,"code":3438,"language":41,"meta":42,"style":42},"\u002F\u002F err1: getError(false) возвращает var err *MyError = nil.\n\u002F\u002F При возврате через interface error создаётся iface:\n\u002F\u002F tab=*MyError, data=nil.\n\u002F\u002F Интерфейс НЕ nil — тип задан.\n\u002F\u002F err1 == nil → false. Печатает \u003Cnil> потому что данные nil.\n\n\u002F\u002F err2: аналогично, но data указывает на MyError.\n\u002F\u002F err2 == nil → false.\n\n\u002F\u002F err3: getErrorFixed возвращает nil напрямую —\n\u002F\u002F оба поля iface (tab и data) равны nil.\n\u002F\u002F err3 == nil → true.\n\n\u002F\u002F Вывод: никогда не возвращай typed nil через интерфейс.\n\u002F\u002F Всегда возвращай nil напрямую.\n",[19,3440,3441,3446,3451,3456,3461,3466,3470,3475,3480,3484,3489,3494,3499,3503,3508],{"__ignoreMap":42},[46,3442,3443],{"class":48,"line":49},[46,3444,3445],{"class":126},"\u002F\u002F err1: getError(false) возвращает var err *MyError = nil.\n",[46,3447,3448],{"class":48,"line":67},[46,3449,3450],{"class":126},"\u002F\u002F При возврате через interface error создаётся iface:\n",[46,3452,3453],{"class":48,"line":79},[46,3454,3455],{"class":126},"\u002F\u002F tab=*MyError, data=nil.\n",[46,3457,3458],{"class":48,"line":85},[46,3459,3460],{"class":126},"\u002F\u002F Интерфейс НЕ nil — тип задан.\n",[46,3462,3463],{"class":48,"line":92},[46,3464,3465],{"class":126},"\u002F\u002F err1 == nil → false. Печатает \u003Cnil> потому что данные nil.\n",[46,3467,3468],{"class":48,"line":105},[46,3469,89],{"emptyLinePlaceholder":88},[46,3471,3472],{"class":48,"line":113},[46,3473,3474],{"class":126},"\u002F\u002F err2: аналогично, но data указывает на MyError.\n",[46,3476,3477],{"class":48,"line":118},[46,3478,3479],{"class":126},"\u002F\u002F err2 == nil → false.\n",[46,3481,3482],{"class":48,"line":123},[46,3483,89],{"emptyLinePlaceholder":88},[46,3485,3486],{"class":48,"line":130},[46,3487,3488],{"class":126},"\u002F\u002F err3: getErrorFixed возвращает nil напрямую —\n",[46,3490,3491],{"class":48,"line":136},[46,3492,3493],{"class":126},"\u002F\u002F оба поля iface (tab и data) равны nil.\n",[46,3495,3496],{"class":48,"line":165},[46,3497,3498],{"class":126},"\u002F\u002F err3 == nil → true.\n",[46,3500,3501],{"class":48,"line":174},[46,3502,89],{"emptyLinePlaceholder":88},[46,3504,3505],{"class":48,"line":179},[46,3506,3507],{"class":126},"\u002F\u002F Вывод: никогда не возвращай typed nil через интерфейс.\n",[46,3509,3510],{"class":48,"line":184},[46,3511,3512],{"class":126},"\u002F\u002F Всегда возвращай nil напрямую.\n",[29,3514],{},[15,3516,3517],{},[24,3518,3519],{},"Задача 3: Цепочка middleware через интерфейс",[15,3521,3522,3524],{},[24,3523,2544],{}," Сложная",[15,3526,3527,3529],{},[24,3528,2550],{}," практическое применение интерфейсов, паттерн декоратор",[15,3531,3532,3534,3535,3538,3539,3542,3543,3546,3547,3550],{},[24,3533,2556],{}," Реализуй систему middleware для обработки сообщений. Есть интерфейс ",[19,3536,3537],{},"Processor",". Реализуй три обёртки: ",[19,3540,3541],{},"LoggingProcessor"," (логирует сообщение до и после), ",[19,3544,3545],{},"UpperCaseProcessor"," (переводит в верхний регистр) и ",[19,3548,3549],{},"TrimProcessor"," (убирает пробелы). Обёртки должны комбинироваться в цепочку.",[37,3552,3554],{"className":39,"code":3553,"language":41,"meta":42,"style":42},"type Processor interface {\n    Process(msg string) string\n}\n",[19,3555,3556,3567,3583],{"__ignoreMap":42},[46,3557,3558,3560,3563,3565],{"class":48,"line":49},[46,3559,53],{"class":52},[46,3561,3562],{"class":56}," Processor",[46,3564,60],{"class":52},[46,3566,64],{"class":63},[46,3568,3569,3572,3574,3577,3579,3581],{"class":48,"line":67},[46,3570,3571],{"class":56},"    Process",[46,3573,632],{"class":63},[46,3575,3576],{"class":145},"msg",[46,3578,971],{"class":52},[46,3580,152],{"class":63},[46,3582,76],{"class":52},[46,3584,3585],{"class":48,"line":79},[46,3586,82],{"class":63},[15,3588,3589],{},[24,3590,2619],{},[37,3592,3595],{"className":3593,"code":3594,"language":2625},[2623],"base := &BaseProcessor{}\nchain := NewLogging(NewUpperCase(NewTrim(base)))\nchain.Process(\"  hello world  \")\n→ [LOG] processing: \"  hello world  \"\n→ [LOG] result: \"HELLO WORLD\"\n→ \"HELLO WORLD\"\n",[19,3596,3594],{"__ignoreMap":42},[15,3598,3599],{},[24,3600,2632],{},[37,3602,3604],{"className":39,"code":3603,"language":41,"meta":42,"style":42},"package main\n\nimport (\n    \"fmt\"\n    \"strings\"\n)\n\ntype Processor interface {\n    Process(msg string) string\n}\n\n\u002F\u002F Базовый процессор — возвращает сообщение как есть\ntype BaseProcessor struct{}\n\nfunc (b *BaseProcessor) Process(msg string) string { return msg }\n\n\u002F\u002F Trim обёртка\ntype TrimProcessor struct{ next Processor }\n\nfunc NewTrim(next Processor) *TrimProcessor { return &TrimProcessor{next: next} }\n\nfunc (t *TrimProcessor) Process(msg string) string {\n    return t.next.Process(strings.TrimSpace(msg))\n}\n\n\u002F\u002F UpperCase обёртка\ntype UpperCaseProcessor struct{ next Processor }\n\nfunc NewUpperCase(next Processor) *UpperCaseProcessor {\n    return &UpperCaseProcessor{next: next}\n}\n\nfunc (u *UpperCaseProcessor) Process(msg string) string {\n    return u.next.Process(strings.ToUpper(msg))\n}\n\n\u002F\u002F Logging обёртка\ntype LoggingProcessor struct{ next Processor }\n\nfunc NewLogging(next Processor) *LoggingProcessor {\n    return &LoggingProcessor{next: next}\n}\n\nfunc (l *LoggingProcessor) Process(msg string) string {\n    fmt.Printf(\"[LOG] processing: %q\\n\", msg)\n    result := l.next.Process(msg)\n    fmt.Printf(\"[LOG] result: %q\\n\", result)\n    return result\n}\n\nfunc main() {\n    base := &BaseProcessor{}\n    chain := NewLogging(NewUpperCase(NewTrim(base)))\n    result := chain.Process(\"  hello world  \")\n    fmt.Println(result)\n}\n",[19,3605,3606,3612,3616,3622,3630,3639,3643,3647,3657,3671,3675,3679,3684,3695,3699,3735,3739,3744,3760,3764,3795,3799,3828,3846,3850,3854,3859,3874,3878,3899,3910,3914,3918,3946,3963,3968,3973,3979,3995,4000,4022,4033,4038,4043,4073,4093,4109,4128,4136,4141,4146,4155,4169,4192,4211,4221],{"__ignoreMap":42},[46,3607,3608,3610],{"class":48,"line":49},[46,3609,1893],{"class":52},[46,3611,2644],{"class":56},[46,3613,3614],{"class":48,"line":67},[46,3615,89],{"emptyLinePlaceholder":88},[46,3617,3618,3620],{"class":48,"line":79},[46,3619,2653],{"class":52},[46,3621,2656],{"class":63},[46,3623,3624,3626,3628],{"class":48,"line":85},[46,3625,2661],{"class":204},[46,3627,2664],{"class":56},[46,3629,2667],{"class":204},[46,3631,3632,3634,3637],{"class":48,"line":92},[46,3633,2661],{"class":204},[46,3635,3636],{"class":56},"strings",[46,3638,2667],{"class":204},[46,3640,3641],{"class":48,"line":105},[46,3642,661],{"class":63},[46,3644,3645],{"class":48,"line":113},[46,3646,89],{"emptyLinePlaceholder":88},[46,3648,3649,3651,3653,3655],{"class":48,"line":118},[46,3650,53],{"class":52},[46,3652,3562],{"class":56},[46,3654,60],{"class":52},[46,3656,64],{"class":63},[46,3658,3659,3661,3663,3665,3667,3669],{"class":48,"line":123},[46,3660,3571],{"class":56},[46,3662,632],{"class":63},[46,3664,3576],{"class":145},[46,3666,971],{"class":52},[46,3668,152],{"class":63},[46,3670,76],{"class":52},[46,3672,3673],{"class":48,"line":130},[46,3674,82],{"class":63},[46,3676,3677],{"class":48,"line":136},[46,3678,89],{"emptyLinePlaceholder":88},[46,3680,3681],{"class":48,"line":165},[46,3682,3683],{"class":126},"\u002F\u002F Базовый процессор — возвращает сообщение как есть\n",[46,3685,3686,3688,3691,3693],{"class":48,"line":174},[46,3687,53],{"class":52},[46,3689,3690],{"class":56}," BaseProcessor",[46,3692,100],{"class":52},[46,3694,1823],{"class":63},[46,3696,3697],{"class":48,"line":179},[46,3698,89],{"emptyLinePlaceholder":88},[46,3700,3701,3703,3705,3708,3710,3713,3715,3718,3720,3722,3724,3726,3728,3730,3732],{"class":48,"line":184},[46,3702,139],{"class":52},[46,3704,142],{"class":63},[46,3706,3707],{"class":145},"b ",[46,3709,276],{"class":52},[46,3711,3712],{"class":56},"BaseProcessor",[46,3714,152],{"class":63},[46,3716,3717],{"class":56},"Process",[46,3719,632],{"class":63},[46,3721,3576],{"class":145},[46,3723,971],{"class":52},[46,3725,152],{"class":63},[46,3727,160],{"class":52},[46,3729,1808],{"class":63},[46,3731,2781],{"class":52},[46,3733,3734],{"class":63}," msg }\n",[46,3736,3737],{"class":48,"line":214},[46,3738,89],{"emptyLinePlaceholder":88},[46,3740,3741],{"class":48,"line":1717},[46,3742,3743],{"class":126},"\u002F\u002F Trim обёртка\n",[46,3745,3746,3748,3751,3753,3756,3758],{"class":48,"line":2082},[46,3747,53],{"class":52},[46,3749,3750],{"class":56}," TrimProcessor",[46,3752,100],{"class":52},[46,3754,3755],{"class":63},"{ next ",[46,3757,3537],{"class":56},[46,3759,1956],{"class":63},[46,3761,3762],{"class":48,"line":2271},[46,3763,89],{"emptyLinePlaceholder":88},[46,3765,3766,3768,3771,3773,3776,3778,3780,3782,3784,3786,3788,3790,3792],{"class":48,"line":2276},[46,3767,139],{"class":52},[46,3769,3770],{"class":56}," NewTrim",[46,3772,632],{"class":63},[46,3774,3775],{"class":145},"next",[46,3777,3562],{"class":56},[46,3779,152],{"class":63},[46,3781,276],{"class":52},[46,3783,3549],{"class":56},[46,3785,1808],{"class":63},[46,3787,2781],{"class":52},[46,3789,588],{"class":52},[46,3791,3549],{"class":56},[46,3793,3794],{"class":63},"{next: next} }\n",[46,3796,3797],{"class":48,"line":2301},[46,3798,89],{"emptyLinePlaceholder":88},[46,3800,3801,3803,3805,3808,3810,3812,3814,3816,3818,3820,3822,3824,3826],{"class":48,"line":2315},[46,3802,139],{"class":52},[46,3804,142],{"class":63},[46,3806,3807],{"class":145},"t ",[46,3809,276],{"class":52},[46,3811,3549],{"class":56},[46,3813,152],{"class":63},[46,3815,3717],{"class":56},[46,3817,632],{"class":63},[46,3819,3576],{"class":145},[46,3821,971],{"class":52},[46,3823,152],{"class":63},[46,3825,160],{"class":52},[46,3827,64],{"class":63},[46,3829,3830,3832,3835,3837,3840,3843],{"class":48,"line":2328},[46,3831,168],{"class":52},[46,3833,3834],{"class":63}," t.next.",[46,3836,3717],{"class":56},[46,3838,3839],{"class":63},"(strings.",[46,3841,3842],{"class":56},"TrimSpace",[46,3844,3845],{"class":63},"(msg))\n",[46,3847,3848],{"class":48,"line":2343},[46,3849,82],{"class":63},[46,3851,3852],{"class":48,"line":2360},[46,3853,89],{"emptyLinePlaceholder":88},[46,3855,3856],{"class":48,"line":2963},[46,3857,3858],{"class":126},"\u002F\u002F UpperCase обёртка\n",[46,3860,3861,3863,3866,3868,3870,3872],{"class":48,"line":2968},[46,3862,53],{"class":52},[46,3864,3865],{"class":56}," UpperCaseProcessor",[46,3867,100],{"class":52},[46,3869,3755],{"class":63},[46,3871,3537],{"class":56},[46,3873,1956],{"class":63},[46,3875,3876],{"class":48,"line":2975},[46,3877,89],{"emptyLinePlaceholder":88},[46,3879,3880,3882,3885,3887,3889,3891,3893,3895,3897],{"class":48,"line":2980},[46,3881,139],{"class":52},[46,3883,3884],{"class":56}," NewUpperCase",[46,3886,632],{"class":63},[46,3888,3775],{"class":145},[46,3890,3562],{"class":56},[46,3892,152],{"class":63},[46,3894,276],{"class":52},[46,3896,3545],{"class":56},[46,3898,64],{"class":63},[46,3900,3901,3903,3905,3907],{"class":48,"line":2985},[46,3902,168],{"class":52},[46,3904,588],{"class":52},[46,3906,3545],{"class":56},[46,3908,3909],{"class":63},"{next: next}\n",[46,3911,3912],{"class":48,"line":2996},[46,3913,82],{"class":63},[46,3915,3916],{"class":48,"line":3038},[46,3917,89],{"emptyLinePlaceholder":88},[46,3919,3920,3922,3924,3926,3928,3930,3932,3934,3936,3938,3940,3942,3944],{"class":48,"line":3066},[46,3921,139],{"class":52},[46,3923,142],{"class":63},[46,3925,146],{"class":145},[46,3927,276],{"class":52},[46,3929,3545],{"class":56},[46,3931,152],{"class":63},[46,3933,3717],{"class":56},[46,3935,632],{"class":63},[46,3937,3576],{"class":145},[46,3939,971],{"class":52},[46,3941,152],{"class":63},[46,3943,160],{"class":52},[46,3945,64],{"class":63},[46,3947,3949,3951,3954,3956,3958,3961],{"class":48,"line":3948},34,[46,3950,168],{"class":52},[46,3952,3953],{"class":63}," u.next.",[46,3955,3717],{"class":56},[46,3957,3839],{"class":63},[46,3959,3960],{"class":56},"ToUpper",[46,3962,3845],{"class":63},[46,3964,3966],{"class":48,"line":3965},35,[46,3967,82],{"class":63},[46,3969,3971],{"class":48,"line":3970},36,[46,3972,89],{"emptyLinePlaceholder":88},[46,3974,3976],{"class":48,"line":3975},37,[46,3977,3978],{"class":126},"\u002F\u002F Logging обёртка\n",[46,3980,3982,3984,3987,3989,3991,3993],{"class":48,"line":3981},38,[46,3983,53],{"class":52},[46,3985,3986],{"class":56}," LoggingProcessor",[46,3988,100],{"class":52},[46,3990,3755],{"class":63},[46,3992,3537],{"class":56},[46,3994,1956],{"class":63},[46,3996,3998],{"class":48,"line":3997},39,[46,3999,89],{"emptyLinePlaceholder":88},[46,4001,4003,4005,4008,4010,4012,4014,4016,4018,4020],{"class":48,"line":4002},40,[46,4004,139],{"class":52},[46,4006,4007],{"class":56}," NewLogging",[46,4009,632],{"class":63},[46,4011,3775],{"class":145},[46,4013,3562],{"class":56},[46,4015,152],{"class":63},[46,4017,276],{"class":52},[46,4019,3541],{"class":56},[46,4021,64],{"class":63},[46,4023,4025,4027,4029,4031],{"class":48,"line":4024},41,[46,4026,168],{"class":52},[46,4028,588],{"class":52},[46,4030,3541],{"class":56},[46,4032,3909],{"class":63},[46,4034,4036],{"class":48,"line":4035},42,[46,4037,82],{"class":63},[46,4039,4041],{"class":48,"line":4040},43,[46,4042,89],{"emptyLinePlaceholder":88},[46,4044,4046,4048,4050,4053,4055,4057,4059,4061,4063,4065,4067,4069,4071],{"class":48,"line":4045},44,[46,4047,139],{"class":52},[46,4049,142],{"class":63},[46,4051,4052],{"class":145},"l ",[46,4054,276],{"class":52},[46,4056,3541],{"class":56},[46,4058,152],{"class":63},[46,4060,3717],{"class":56},[46,4062,632],{"class":63},[46,4064,3576],{"class":145},[46,4066,971],{"class":52},[46,4068,152],{"class":63},[46,4070,160],{"class":52},[46,4072,64],{"class":63},[46,4074,4076,4078,4080,4082,4085,4088,4090],{"class":48,"line":4075},45,[46,4077,1212],{"class":63},[46,4079,3043],{"class":56},[46,4081,632],{"class":63},[46,4083,4084],{"class":204},"\"[LOG] processing: ",[46,4086,4087],{"class":284},"%q\\n",[46,4089,1359],{"class":204},[46,4091,4092],{"class":63},", msg)\n",[46,4094,4096,4099,4101,4104,4106],{"class":48,"line":4095},46,[46,4097,4098],{"class":63},"    result ",[46,4100,875],{"class":52},[46,4102,4103],{"class":63}," l.next.",[46,4105,3717],{"class":56},[46,4107,4108],{"class":63},"(msg)\n",[46,4110,4112,4114,4116,4118,4121,4123,4125],{"class":48,"line":4111},47,[46,4113,1212],{"class":63},[46,4115,3043],{"class":56},[46,4117,632],{"class":63},[46,4119,4120],{"class":204},"\"[LOG] result: ",[46,4122,4087],{"class":284},[46,4124,1359],{"class":204},[46,4126,4127],{"class":63},", result)\n",[46,4129,4131,4133],{"class":48,"line":4130},48,[46,4132,168],{"class":52},[46,4134,4135],{"class":63}," result\n",[46,4137,4139],{"class":48,"line":4138},49,[46,4140,82],{"class":63},[46,4142,4144],{"class":48,"line":4143},50,[46,4145,89],{"emptyLinePlaceholder":88},[46,4147,4149,4151,4153],{"class":48,"line":4148},51,[46,4150,139],{"class":52},[46,4152,2990],{"class":56},[46,4154,2993],{"class":63},[46,4156,4158,4161,4163,4165,4167],{"class":48,"line":4157},52,[46,4159,4160],{"class":63},"    base ",[46,4162,875],{"class":52},[46,4164,588],{"class":52},[46,4166,3712],{"class":56},[46,4168,1823],{"class":63},[46,4170,4172,4175,4177,4179,4181,4184,4186,4189],{"class":48,"line":4171},53,[46,4173,4174],{"class":63},"    chain ",[46,4176,875],{"class":52},[46,4178,4007],{"class":56},[46,4180,632],{"class":63},[46,4182,4183],{"class":56},"NewUpperCase",[46,4185,632],{"class":63},[46,4187,4188],{"class":56},"NewTrim",[46,4190,4191],{"class":63},"(base)))\n",[46,4193,4195,4197,4199,4202,4204,4206,4209],{"class":48,"line":4194},54,[46,4196,4098],{"class":63},[46,4198,875],{"class":52},[46,4200,4201],{"class":63}," chain.",[46,4203,3717],{"class":56},[46,4205,632],{"class":63},[46,4207,4208],{"class":204},"\"  hello world  \"",[46,4210,661],{"class":63},[46,4212,4214,4216,4218],{"class":48,"line":4213},55,[46,4215,1212],{"class":63},[46,4217,220],{"class":56},[46,4219,4220],{"class":63},"(result)\n",[46,4222,4224],{"class":48,"line":4223},56,[46,4225,82],{"class":63},[29,4227],{},[4229,4230,4231],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}",{"title":42,"searchDepth":67,"depth":67,"links":4233},[4234,4235,4240,4241,4242,4243,4244,4245,4246,4251],{"id":34,"depth":67,"text":35},{"id":298,"depth":67,"text":299,"children":4236},[4237,4239],{"id":310,"depth":79,"text":4238},"eface — пустой интерфейс (interface{} \u002F any)",{"id":384,"depth":79,"text":385},{"id":520,"depth":67,"text":521},{"id":604,"depth":67,"text":605},{"id":772,"depth":67,"text":773},{"id":1117,"depth":67,"text":1118},{"id":1280,"depth":67,"text":1281},{"id":1506,"depth":67,"text":1507},{"id":1724,"depth":67,"text":1725,"children":4247},[4248,4249,4250],{"id":1728,"depth":79,"text":1729},{"id":1870,"depth":79,"text":1871},{"id":2090,"depth":79,"text":2091},{"id":2367,"depth":67,"text":2368},"Интерфейсы — это то, что делает Go-код по-настоящему гибким. В большинстве языков для реализации интерфейса нужно явно написать implements. В Go достаточно просто иметь нужные методы — это называется structural typing или duck typing. За этой простотой снаружи скрывается нетривиальное устройство внутри, которое важно понимать, чтобы не попасть в классические ловушки.","beginner","md",{},"basics","functions","\u002F01-basics\u002F05-interfaces","structs",{"title":5,"description":4252},"interfaces","01-basics\u002F05-interfaces\u002Findex",[4264,496,374,4265,4266,4267,4268],"интерфейс","duck typing","nil interface","type assertion","собеседование","wA2QxqMAAhbm0kPnzsrYeMaJPGiO7Z7b337gO_j-If0",1776289836331]