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