[{"data":1,"prerenderedAt":7519},["ShallowReactive",2],{"content:\u002F02-functions-errors\u002F03-errors":3},{"title":4,"description":5,"path":6,"body":7},"Ошибки в Go","","\u002F02-functions-errors\u002F03-errors",{"type":8,"value":9,"toc":7457},"minimark",[10,81,84,89,94,147,154,158,342,345,349,593,597,842,846,1054,1057,1059,1099,1101,1105,1109,1486,1492,1496,1616,1619,1623,1770,1773,1784,1788,1945,1948,1952,1966,1968,2005,2007,2011,2015,2184,2206,2210,2360,2369,2373,2684,2690,2697,2701,2857,2870,2872,2912,2914,2918,2922,3023,3031,3035,3310,3315,3319,3536,3539,3543,3718,3721,3725,3729,3810,3813,3815,3848,3850,3854,3885,3889,4394,4398,4408,4412,4594,4596,4635,4637,4641,4645,4802,4806,4814,4818,4949,4971,4975,5077,5081,5091,5095,5098,5100,5134,5136,5140,5144,5332,5335,5339,5631,5634,5638,5691,5695,5953,5956,5960,5964,5967,5969,6003,6005,6009,6302,6305,6310,6314,6503,6510,6514,6518,6730,6732,6761,6763,6767,6870,6877,6881,6887,6897,6901,6907,6917,6921,6928,6930,6934,6983,7129,7453],[11,12,13,24,35,43,62,74],"ul",{},[14,15,16,20,21],"li",{},[17,18,19],"code",{},"error"," — встроенный интерфейс с единственным методом ",[17,22,23],{},"Error() string",[14,25,26,27,30,31,34],{},"создание: ",[17,28,29],{},"errors.New(\"text\")"," — простая ошибка; ",[17,32,33],{},"fmt.Errorf(\"... %v\", arg)"," — с форматированием",[14,36,37,38,42],{},"ошибку ",[39,40,41],"strong",{},"возвращают последней"," из функции — соглашение Go",[14,44,45,46,49,50,53,54,57,58,61],{},"именование переменных: начинаются с ",[17,47,48],{},"Err","\u002F",[17,51,52],{},"err"," (",[17,55,56],{},"ErrNotFound",", ",[17,59,60],{},"errInternal",")",[14,63,64,65,53,68,57,71,61],{},"именование типов: заканчиваются на ",[17,66,67],{},"Error",[17,69,70],{},"PathError",[17,72,73],{},"SyntaxError",[14,75,76,77,80],{},"текст ошибки — ",[39,78,79],{},"с маленькой буквы"," (соглашение гошного коммьюнити)",[82,83],"hr",{},[85,86,88],"h2",{"id":87},"базовая-модель-error","Базовая модель error",[90,91,93],"h3",{"id":92},"интерфейс","Интерфейс",[95,96,100],"pre",{"className":97,"code":98,"language":99,"meta":5,"style":5},"language-go shiki shiki-themes github-dark","\u002F\u002F builtin\ntype error interface {\n    Error() string\n}\n","go",[17,101,102,111,128,141],{"__ignoreMap":5},[103,104,107],"span",{"class":105,"line":106},"line",1,[103,108,110],{"class":109},"sAwPA","\u002F\u002F builtin\n",[103,112,114,118,121,124],{"class":105,"line":113},2,[103,115,117],{"class":116},"snl16","type",[103,119,120],{"class":116}," error",[103,122,123],{"class":116}," interface",[103,125,127],{"class":126},"s95oV"," {\n",[103,129,131,135,138],{"class":105,"line":130},3,[103,132,134],{"class":133},"svObZ","    Error",[103,136,137],{"class":126},"() ",[103,139,140],{"class":116},"string\n",[103,142,144],{"class":105,"line":143},4,[103,145,146],{"class":126},"}\n",[148,149,150,151,153],"p",{},"Любой тип с методом ",[17,152,23],{}," реализует этот интерфейс.",[90,155,157],{"id":156},"создание-ошибок","Создание ошибок",[95,159,161],{"className":97,"code":160,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc main() {\n    \u002F\u002F Простая ошибка — достаточно в большинстве случаев\n    err1 := errors.New(\"connection refused\")\n\n    \u002F\u002F С форматированием — когда нужны параметры в тексте\n    userID := 42\n    err2 := fmt.Errorf(\"user %d not found\", userID)\n\n    fmt.Println(err1)\n    fmt.Println(err2)\n}\n",[17,162,163,171,177,185,197,207,213,218,230,236,259,264,270,282,310,315,327,337],{"__ignoreMap":5},[103,164,165,168],{"class":105,"line":106},[103,166,167],{"class":116},"package",[103,169,170],{"class":133}," main\n",[103,172,173],{"class":105,"line":113},[103,174,176],{"emptyLinePlaceholder":175},true,"\n",[103,178,179,182],{"class":105,"line":130},[103,180,181],{"class":116},"import",[103,183,184],{"class":126}," (\n",[103,186,187,191,194],{"class":105,"line":143},[103,188,190],{"class":189},"sU2Wk","    \"",[103,192,193],{"class":133},"errors",[103,195,196],{"class":189},"\"\n",[103,198,200,202,205],{"class":105,"line":199},5,[103,201,190],{"class":189},[103,203,204],{"class":133},"fmt",[103,206,196],{"class":189},[103,208,210],{"class":105,"line":209},6,[103,211,212],{"class":126},")\n",[103,214,216],{"class":105,"line":215},7,[103,217,176],{"emptyLinePlaceholder":175},[103,219,221,224,227],{"class":105,"line":220},8,[103,222,223],{"class":116},"func",[103,225,226],{"class":133}," main",[103,228,229],{"class":126},"() {\n",[103,231,233],{"class":105,"line":232},9,[103,234,235],{"class":109},"    \u002F\u002F Простая ошибка — достаточно в большинстве случаев\n",[103,237,239,242,245,248,251,254,257],{"class":105,"line":238},10,[103,240,241],{"class":126},"    err1 ",[103,243,244],{"class":116},":=",[103,246,247],{"class":126}," errors.",[103,249,250],{"class":133},"New",[103,252,253],{"class":126},"(",[103,255,256],{"class":189},"\"connection refused\"",[103,258,212],{"class":126},[103,260,262],{"class":105,"line":261},11,[103,263,176],{"emptyLinePlaceholder":175},[103,265,267],{"class":105,"line":266},12,[103,268,269],{"class":109},"    \u002F\u002F С форматированием — когда нужны параметры в тексте\n",[103,271,273,276,278],{"class":105,"line":272},13,[103,274,275],{"class":126},"    userID ",[103,277,244],{"class":116},[103,279,281],{"class":280},"sDLfK"," 42\n",[103,283,285,288,290,293,296,298,301,304,307],{"class":105,"line":284},14,[103,286,287],{"class":126},"    err2 ",[103,289,244],{"class":116},[103,291,292],{"class":126}," fmt.",[103,294,295],{"class":133},"Errorf",[103,297,253],{"class":126},[103,299,300],{"class":189},"\"user ",[103,302,303],{"class":280},"%d",[103,305,306],{"class":189}," not found\"",[103,308,309],{"class":126},", userID)\n",[103,311,313],{"class":105,"line":312},15,[103,314,176],{"emptyLinePlaceholder":175},[103,316,318,321,324],{"class":105,"line":317},16,[103,319,320],{"class":126},"    fmt.",[103,322,323],{"class":133},"Println",[103,325,326],{"class":126},"(err1)\n",[103,328,330,332,334],{"class":105,"line":329},17,[103,331,320],{"class":126},[103,333,323],{"class":133},[103,335,336],{"class":126},"(err2)\n",[103,338,340],{"class":105,"line":339},18,[103,341,146],{"class":126},[148,343,344],{},"На практике чаще всего используют именно эти два способа — отдельные типы нужны редко.",[90,346,348],{"id":347},"соглашения-именования","Соглашения именования",[95,350,352],{"className":97,"code":351,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\n\u002F\u002F Переменные — начинаются с Err\u002Ferr\nvar ErrNotFound = errors.New(\"not found\")      \u002F\u002F публичная\nvar errInternal = errors.New(\"internal error\") \u002F\u002F приватная\n\n\u002F\u002F Типы — заканчиваются на Error\ntype PathError struct {\n    Op, Path string\n    Err      error\n}\n\ntype SyntaxError struct {\n    Msg    string\n    Offset int64\n}\n\nfunc main() {\n    \u002F\u002F Текст — с маленькой буквы\n    fmt.Println(errors.New(\"connection refused\")) \u002F\u002F ✅\n    fmt.Println(ErrNotFound)\n    fmt.Println(errInternal)\n}\n",[17,353,354,360,364,370,378,386,390,394,399,425,449,453,458,470,477,485,489,493,504,512,521,526,531,540,546,568,578,588],{"__ignoreMap":5},[103,355,356,358],{"class":105,"line":106},[103,357,167],{"class":116},[103,359,170],{"class":133},[103,361,362],{"class":105,"line":113},[103,363,176],{"emptyLinePlaceholder":175},[103,365,366,368],{"class":105,"line":130},[103,367,181],{"class":116},[103,369,184],{"class":126},[103,371,372,374,376],{"class":105,"line":143},[103,373,190],{"class":189},[103,375,193],{"class":133},[103,377,196],{"class":189},[103,379,380,382,384],{"class":105,"line":199},[103,381,190],{"class":189},[103,383,204],{"class":133},[103,385,196],{"class":189},[103,387,388],{"class":105,"line":209},[103,389,212],{"class":126},[103,391,392],{"class":105,"line":215},[103,393,176],{"emptyLinePlaceholder":175},[103,395,396],{"class":105,"line":220},[103,397,398],{"class":109},"\u002F\u002F Переменные — начинаются с Err\u002Ferr\n",[103,400,401,404,407,410,412,414,416,419,422],{"class":105,"line":232},[103,402,403],{"class":116},"var",[103,405,406],{"class":126}," ErrNotFound ",[103,408,409],{"class":116},"=",[103,411,247],{"class":126},[103,413,250],{"class":133},[103,415,253],{"class":126},[103,417,418],{"class":189},"\"not found\"",[103,420,421],{"class":126},")      ",[103,423,424],{"class":109},"\u002F\u002F публичная\n",[103,426,427,429,432,434,436,438,440,443,446],{"class":105,"line":238},[103,428,403],{"class":116},[103,430,431],{"class":126}," errInternal ",[103,433,409],{"class":116},[103,435,247],{"class":126},[103,437,250],{"class":133},[103,439,253],{"class":126},[103,441,442],{"class":189},"\"internal error\"",[103,444,445],{"class":126},") ",[103,447,448],{"class":109},"\u002F\u002F приватная\n",[103,450,451],{"class":105,"line":261},[103,452,176],{"emptyLinePlaceholder":175},[103,454,455],{"class":105,"line":266},[103,456,457],{"class":109},"\u002F\u002F Типы — заканчиваются на Error\n",[103,459,460,462,465,468],{"class":105,"line":272},[103,461,117],{"class":116},[103,463,464],{"class":133}," PathError",[103,466,467],{"class":116}," struct",[103,469,127],{"class":126},[103,471,472,475],{"class":105,"line":284},[103,473,474],{"class":126},"    Op, Path ",[103,476,140],{"class":116},[103,478,479,482],{"class":105,"line":312},[103,480,481],{"class":126},"    Err      ",[103,483,484],{"class":116},"error\n",[103,486,487],{"class":105,"line":317},[103,488,146],{"class":126},[103,490,491],{"class":105,"line":329},[103,492,176],{"emptyLinePlaceholder":175},[103,494,495,497,500,502],{"class":105,"line":339},[103,496,117],{"class":116},[103,498,499],{"class":133}," SyntaxError",[103,501,467],{"class":116},[103,503,127],{"class":126},[103,505,507,510],{"class":105,"line":506},19,[103,508,509],{"class":126},"    Msg    ",[103,511,140],{"class":116},[103,513,515,518],{"class":105,"line":514},20,[103,516,517],{"class":126},"    Offset ",[103,519,520],{"class":116},"int64\n",[103,522,524],{"class":105,"line":523},21,[103,525,146],{"class":126},[103,527,529],{"class":105,"line":528},22,[103,530,176],{"emptyLinePlaceholder":175},[103,532,534,536,538],{"class":105,"line":533},23,[103,535,223],{"class":116},[103,537,226],{"class":133},[103,539,229],{"class":126},[103,541,543],{"class":105,"line":542},24,[103,544,545],{"class":109},"    \u002F\u002F Текст — с маленькой буквы\n",[103,547,549,551,553,556,558,560,562,565],{"class":105,"line":548},25,[103,550,320],{"class":126},[103,552,323],{"class":133},[103,554,555],{"class":126},"(errors.",[103,557,250],{"class":133},[103,559,253],{"class":126},[103,561,256],{"class":189},[103,563,564],{"class":126},")) ",[103,566,567],{"class":109},"\u002F\u002F ✅\n",[103,569,571,573,575],{"class":105,"line":570},26,[103,572,320],{"class":126},[103,574,323],{"class":133},[103,576,577],{"class":126},"(ErrNotFound)\n",[103,579,581,583,585],{"class":105,"line":580},27,[103,582,320],{"class":126},[103,584,323],{"class":133},[103,586,587],{"class":126},"(errInternal)\n",[103,589,591],{"class":105,"line":590},28,[103,592,146],{"class":126},[90,594,596],{"id":595},"ошибка-последнее-возвращаемое-значение","Ошибка — последнее возвращаемое значение",[95,598,600],{"className":97,"code":599,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\n\u002F\u002F ✅ принято в Go — ошибка последняя\nfunc ReadFileGood(path string) ([]byte, error) {\n    if path == \"\" {\n        return nil, errors.New(\"empty path\")\n    }\n    return []byte(\"content\"), nil\n}\n\n\u002F\u002F ❌ непривычно, ломает ожидания\nfunc ReadFileBad(path string) (error, []byte) {\n    return nil, []byte(\"content\")\n}\n\nfunc main() {\n    data, err := ReadFileGood(\"config.yaml\")\n    fmt.Println(string(data), err)\n}\n",[17,601,602,608,612,618,626,634,638,642,647,676,692,712,717,738,742,746,751,776,792,796,800,808,824,838],{"__ignoreMap":5},[103,603,604,606],{"class":105,"line":106},[103,605,167],{"class":116},[103,607,170],{"class":133},[103,609,610],{"class":105,"line":113},[103,611,176],{"emptyLinePlaceholder":175},[103,613,614,616],{"class":105,"line":130},[103,615,181],{"class":116},[103,617,184],{"class":126},[103,619,620,622,624],{"class":105,"line":143},[103,621,190],{"class":189},[103,623,193],{"class":133},[103,625,196],{"class":189},[103,627,628,630,632],{"class":105,"line":199},[103,629,190],{"class":189},[103,631,204],{"class":133},[103,633,196],{"class":189},[103,635,636],{"class":105,"line":209},[103,637,212],{"class":126},[103,639,640],{"class":105,"line":215},[103,641,176],{"emptyLinePlaceholder":175},[103,643,644],{"class":105,"line":220},[103,645,646],{"class":109},"\u002F\u002F ✅ принято в Go — ошибка последняя\n",[103,648,649,651,654,656,660,663,666,669,671,673],{"class":105,"line":232},[103,650,223],{"class":116},[103,652,653],{"class":133}," ReadFileGood",[103,655,253],{"class":126},[103,657,659],{"class":658},"s9osk","path",[103,661,662],{"class":116}," string",[103,664,665],{"class":126},") ([]",[103,667,668],{"class":116},"byte",[103,670,57],{"class":126},[103,672,19],{"class":116},[103,674,675],{"class":126},") {\n",[103,677,678,681,684,687,690],{"class":105,"line":238},[103,679,680],{"class":116},"    if",[103,682,683],{"class":126}," path ",[103,685,686],{"class":116},"==",[103,688,689],{"class":189}," \"\"",[103,691,127],{"class":126},[103,693,694,697,700,703,705,707,710],{"class":105,"line":261},[103,695,696],{"class":116},"        return",[103,698,699],{"class":280}," nil",[103,701,702],{"class":126},", errors.",[103,704,250],{"class":133},[103,706,253],{"class":126},[103,708,709],{"class":189},"\"empty path\"",[103,711,212],{"class":126},[103,713,714],{"class":105,"line":266},[103,715,716],{"class":126},"    }\n",[103,718,719,722,725,727,729,732,735],{"class":105,"line":272},[103,720,721],{"class":116},"    return",[103,723,724],{"class":126}," []",[103,726,668],{"class":116},[103,728,253],{"class":126},[103,730,731],{"class":189},"\"content\"",[103,733,734],{"class":126},"), ",[103,736,737],{"class":280},"nil\n",[103,739,740],{"class":105,"line":284},[103,741,146],{"class":126},[103,743,744],{"class":105,"line":312},[103,745,176],{"emptyLinePlaceholder":175},[103,747,748],{"class":105,"line":317},[103,749,750],{"class":109},"\u002F\u002F ❌ непривычно, ломает ожидания\n",[103,752,753,755,758,760,762,764,767,769,772,774],{"class":105,"line":329},[103,754,223],{"class":116},[103,756,757],{"class":133}," ReadFileBad",[103,759,253],{"class":126},[103,761,659],{"class":658},[103,763,662],{"class":116},[103,765,766],{"class":126},") (",[103,768,19],{"class":116},[103,770,771],{"class":126},", []",[103,773,668],{"class":116},[103,775,675],{"class":126},[103,777,778,780,782,784,786,788,790],{"class":105,"line":339},[103,779,721],{"class":116},[103,781,699],{"class":280},[103,783,771],{"class":126},[103,785,668],{"class":116},[103,787,253],{"class":126},[103,789,731],{"class":189},[103,791,212],{"class":126},[103,793,794],{"class":105,"line":506},[103,795,146],{"class":126},[103,797,798],{"class":105,"line":514},[103,799,176],{"emptyLinePlaceholder":175},[103,801,802,804,806],{"class":105,"line":523},[103,803,223],{"class":116},[103,805,226],{"class":133},[103,807,229],{"class":126},[103,809,810,813,815,817,819,822],{"class":105,"line":528},[103,811,812],{"class":126},"    data, err ",[103,814,244],{"class":116},[103,816,653],{"class":133},[103,818,253],{"class":126},[103,820,821],{"class":189},"\"config.yaml\"",[103,823,212],{"class":126},[103,825,826,828,830,832,835],{"class":105,"line":533},[103,827,320],{"class":126},[103,829,323],{"class":133},[103,831,253],{"class":126},[103,833,834],{"class":116},"string",[103,836,837],{"class":126},"(data), err)\n",[103,839,840],{"class":105,"line":542},[103,841,146],{"class":126},[90,843,845],{"id":844},"антипаттерн-лишнее-проксирование","Антипаттерн: лишнее проксирование",[95,847,849],{"className":97,"code":848,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc doWork() error { return errors.New(\"work failed\") }\n\n\u002F\u002F ❌ бессмысленный код — ничего не меняет\nfunc DoSomethingVerbose() error {\n    err := doWork()\n    if err != nil {\n        return err\n    }\n    return nil\n}\n\n\u002F\u002F ✅ эквивалентно и проще\nfunc DoSomething() error {\n    return doWork()\n}\n\nfunc main() {\n    fmt.Println(DoSomething())\n}\n",[17,850,851,857,861,867,875,883,887,891,920,924,929,942,954,968,975,979,986,990,994,999,1012,1020,1024,1028,1036,1050],{"__ignoreMap":5},[103,852,853,855],{"class":105,"line":106},[103,854,167],{"class":116},[103,856,170],{"class":133},[103,858,859],{"class":105,"line":113},[103,860,176],{"emptyLinePlaceholder":175},[103,862,863,865],{"class":105,"line":130},[103,864,181],{"class":116},[103,866,184],{"class":126},[103,868,869,871,873],{"class":105,"line":143},[103,870,190],{"class":189},[103,872,193],{"class":133},[103,874,196],{"class":189},[103,876,877,879,881],{"class":105,"line":199},[103,878,190],{"class":189},[103,880,204],{"class":133},[103,882,196],{"class":189},[103,884,885],{"class":105,"line":209},[103,886,212],{"class":126},[103,888,889],{"class":105,"line":215},[103,890,176],{"emptyLinePlaceholder":175},[103,892,893,895,898,900,902,905,908,910,912,914,917],{"class":105,"line":220},[103,894,223],{"class":116},[103,896,897],{"class":133}," doWork",[103,899,137],{"class":126},[103,901,19],{"class":116},[103,903,904],{"class":126}," { ",[103,906,907],{"class":116},"return",[103,909,247],{"class":126},[103,911,250],{"class":133},[103,913,253],{"class":126},[103,915,916],{"class":189},"\"work failed\"",[103,918,919],{"class":126},") }\n",[103,921,922],{"class":105,"line":232},[103,923,176],{"emptyLinePlaceholder":175},[103,925,926],{"class":105,"line":238},[103,927,928],{"class":109},"\u002F\u002F ❌ бессмысленный код — ничего не меняет\n",[103,930,931,933,936,938,940],{"class":105,"line":261},[103,932,223],{"class":116},[103,934,935],{"class":133}," DoSomethingVerbose",[103,937,137],{"class":126},[103,939,19],{"class":116},[103,941,127],{"class":126},[103,943,944,947,949,951],{"class":105,"line":266},[103,945,946],{"class":126},"    err ",[103,948,244],{"class":116},[103,950,897],{"class":133},[103,952,953],{"class":126},"()\n",[103,955,956,958,961,964,966],{"class":105,"line":272},[103,957,680],{"class":116},[103,959,960],{"class":126}," err ",[103,962,963],{"class":116},"!=",[103,965,699],{"class":280},[103,967,127],{"class":126},[103,969,970,972],{"class":105,"line":284},[103,971,696],{"class":116},[103,973,974],{"class":126}," err\n",[103,976,977],{"class":105,"line":312},[103,978,716],{"class":126},[103,980,981,983],{"class":105,"line":317},[103,982,721],{"class":116},[103,984,985],{"class":280}," nil\n",[103,987,988],{"class":105,"line":329},[103,989,146],{"class":126},[103,991,992],{"class":105,"line":339},[103,993,176],{"emptyLinePlaceholder":175},[103,995,996],{"class":105,"line":506},[103,997,998],{"class":109},"\u002F\u002F ✅ эквивалентно и проще\n",[103,1000,1001,1003,1006,1008,1010],{"class":105,"line":514},[103,1002,223],{"class":116},[103,1004,1005],{"class":133}," DoSomething",[103,1007,137],{"class":126},[103,1009,19],{"class":116},[103,1011,127],{"class":126},[103,1013,1014,1016,1018],{"class":105,"line":523},[103,1015,721],{"class":116},[103,1017,897],{"class":133},[103,1019,953],{"class":126},[103,1021,1022],{"class":105,"line":528},[103,1023,146],{"class":126},[103,1025,1026],{"class":105,"line":533},[103,1027,176],{"emptyLinePlaceholder":175},[103,1029,1030,1032,1034],{"class":105,"line":542},[103,1031,223],{"class":116},[103,1033,226],{"class":133},[103,1035,229],{"class":126},[103,1037,1038,1040,1042,1044,1047],{"class":105,"line":548},[103,1039,320],{"class":126},[103,1041,323],{"class":133},[103,1043,253],{"class":126},[103,1045,1046],{"class":133},"DoSomething",[103,1048,1049],{"class":126},"())\n",[103,1051,1052],{"class":105,"line":570},[103,1053,146],{"class":126},[148,1055,1056],{},"Если сигнатуры совпадают и контекст не нужен — просто проксируй.",[82,1058],{},[11,1060,1061,1068,1075,1082,1089],{},[14,1062,1063,1064,1067],{},"sentinel (дозорная) ошибка — ",[39,1065,1066],{},"глобальная переменная"," для маркировки конкретной ситуации",[14,1069,1070,1071,1074],{},"нужна, чтобы понять: произошла ли ",[39,1072,1073],{},"именно эта"," ошибка (sql.ErrNoRows, io.EOF)",[14,1076,1077,1078,1081],{},"проблема: глобальную переменную ",[39,1079,1080],{},"можно изменить"," из другого пакета",[14,1083,1084,1085,1088],{},"решение: ",[39,1086,1087],{},"константные ошибки"," — type definition от string + метод Error()",[14,1090,1091,1092,57,1095,1098],{},"сравнение через ",[17,1093,1094],{},"errors.Is",[39,1096,1097],{},"не"," через ==",[82,1100],{},[85,1102,1104],{"id":1103},"sentinel-ошибки","Sentinel-ошибки",[90,1106,1108],{"id":1107},"sentinel-ошибка","Sentinel ошибка",[95,1110,1112],{"className":97,"code":1111,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\n\u002F\u002F Зарезервированная глобальная ошибка (аналог sql.ErrNoRows)\nvar ErrNoRows = errors.New(\"no rows in result set\")\nvar ErrDatabaseProblem = errors.New(\"database problem\")\n\ntype User struct{ Name string }\n\n\u002F\u002F Имитация запроса к БД\nfunc queryRow(id int) error {\n    if id == 0 {\n        return ErrNoRows\n    }\n    return nil\n}\n\nfunc QueryUser(id int) (*User, error) {\n    err := queryRow(id)\n    if errors.Is(err, ErrNoRows) {\n        return nil, ErrDatabaseProblem\n    }\n    if err != nil {\n        return nil, err\n    }\n    return &User{Name: \"Alice\"}, nil\n}\n\nfunc main() {\n    user, err := QueryUser(0)\n    fmt.Println(user, err) \u002F\u002F \u003Cnil> database problem\n\n    user, err = QueryUser(1)\n    fmt.Println(user, err) \u002F\u002F &{Alice} \u003Cnil>\n}\n",[17,1113,1114,1120,1124,1130,1138,1146,1150,1154,1159,1179,1199,1203,1220,1224,1229,1250,1264,1271,1275,1281,1285,1289,1316,1327,1339,1348,1352,1364,1373,1378,1399,1404,1409,1418,1435,1448,1453,1469,1481],{"__ignoreMap":5},[103,1115,1116,1118],{"class":105,"line":106},[103,1117,167],{"class":116},[103,1119,170],{"class":133},[103,1121,1122],{"class":105,"line":113},[103,1123,176],{"emptyLinePlaceholder":175},[103,1125,1126,1128],{"class":105,"line":130},[103,1127,181],{"class":116},[103,1129,184],{"class":126},[103,1131,1132,1134,1136],{"class":105,"line":143},[103,1133,190],{"class":189},[103,1135,193],{"class":133},[103,1137,196],{"class":189},[103,1139,1140,1142,1144],{"class":105,"line":199},[103,1141,190],{"class":189},[103,1143,204],{"class":133},[103,1145,196],{"class":189},[103,1147,1148],{"class":105,"line":209},[103,1149,212],{"class":126},[103,1151,1152],{"class":105,"line":215},[103,1153,176],{"emptyLinePlaceholder":175},[103,1155,1156],{"class":105,"line":220},[103,1157,1158],{"class":109},"\u002F\u002F Зарезервированная глобальная ошибка (аналог sql.ErrNoRows)\n",[103,1160,1161,1163,1166,1168,1170,1172,1174,1177],{"class":105,"line":232},[103,1162,403],{"class":116},[103,1164,1165],{"class":126}," ErrNoRows ",[103,1167,409],{"class":116},[103,1169,247],{"class":126},[103,1171,250],{"class":133},[103,1173,253],{"class":126},[103,1175,1176],{"class":189},"\"no rows in result set\"",[103,1178,212],{"class":126},[103,1180,1181,1183,1186,1188,1190,1192,1194,1197],{"class":105,"line":238},[103,1182,403],{"class":116},[103,1184,1185],{"class":126}," ErrDatabaseProblem ",[103,1187,409],{"class":116},[103,1189,247],{"class":126},[103,1191,250],{"class":133},[103,1193,253],{"class":126},[103,1195,1196],{"class":189},"\"database problem\"",[103,1198,212],{"class":126},[103,1200,1201],{"class":105,"line":261},[103,1202,176],{"emptyLinePlaceholder":175},[103,1204,1205,1207,1210,1212,1215,1217],{"class":105,"line":266},[103,1206,117],{"class":116},[103,1208,1209],{"class":133}," User",[103,1211,467],{"class":116},[103,1213,1214],{"class":126},"{ Name ",[103,1216,834],{"class":116},[103,1218,1219],{"class":126}," }\n",[103,1221,1222],{"class":105,"line":272},[103,1223,176],{"emptyLinePlaceholder":175},[103,1225,1226],{"class":105,"line":284},[103,1227,1228],{"class":109},"\u002F\u002F Имитация запроса к БД\n",[103,1230,1231,1233,1236,1238,1241,1244,1246,1248],{"class":105,"line":312},[103,1232,223],{"class":116},[103,1234,1235],{"class":133}," queryRow",[103,1237,253],{"class":126},[103,1239,1240],{"class":658},"id",[103,1242,1243],{"class":116}," int",[103,1245,445],{"class":126},[103,1247,19],{"class":116},[103,1249,127],{"class":126},[103,1251,1252,1254,1257,1259,1262],{"class":105,"line":317},[103,1253,680],{"class":116},[103,1255,1256],{"class":126}," id ",[103,1258,686],{"class":116},[103,1260,1261],{"class":280}," 0",[103,1263,127],{"class":126},[103,1265,1266,1268],{"class":105,"line":329},[103,1267,696],{"class":116},[103,1269,1270],{"class":126}," ErrNoRows\n",[103,1272,1273],{"class":105,"line":339},[103,1274,716],{"class":126},[103,1276,1277,1279],{"class":105,"line":506},[103,1278,721],{"class":116},[103,1280,985],{"class":280},[103,1282,1283],{"class":105,"line":514},[103,1284,146],{"class":126},[103,1286,1287],{"class":105,"line":523},[103,1288,176],{"emptyLinePlaceholder":175},[103,1290,1291,1293,1296,1298,1300,1302,1304,1307,1310,1312,1314],{"class":105,"line":528},[103,1292,223],{"class":116},[103,1294,1295],{"class":133}," QueryUser",[103,1297,253],{"class":126},[103,1299,1240],{"class":658},[103,1301,1243],{"class":116},[103,1303,766],{"class":126},[103,1305,1306],{"class":116},"*",[103,1308,1309],{"class":133},"User",[103,1311,57],{"class":126},[103,1313,19],{"class":116},[103,1315,675],{"class":126},[103,1317,1318,1320,1322,1324],{"class":105,"line":533},[103,1319,946],{"class":126},[103,1321,244],{"class":116},[103,1323,1235],{"class":133},[103,1325,1326],{"class":126},"(id)\n",[103,1328,1329,1331,1333,1336],{"class":105,"line":542},[103,1330,680],{"class":116},[103,1332,247],{"class":126},[103,1334,1335],{"class":133},"Is",[103,1337,1338],{"class":126},"(err, ErrNoRows) {\n",[103,1340,1341,1343,1345],{"class":105,"line":548},[103,1342,696],{"class":116},[103,1344,699],{"class":280},[103,1346,1347],{"class":126},", ErrDatabaseProblem\n",[103,1349,1350],{"class":105,"line":570},[103,1351,716],{"class":126},[103,1353,1354,1356,1358,1360,1362],{"class":105,"line":580},[103,1355,680],{"class":116},[103,1357,960],{"class":126},[103,1359,963],{"class":116},[103,1361,699],{"class":280},[103,1363,127],{"class":126},[103,1365,1366,1368,1370],{"class":105,"line":590},[103,1367,696],{"class":116},[103,1369,699],{"class":280},[103,1371,1372],{"class":126},", err\n",[103,1374,1376],{"class":105,"line":1375},29,[103,1377,716],{"class":126},[103,1379,1381,1383,1386,1388,1391,1394,1397],{"class":105,"line":1380},30,[103,1382,721],{"class":116},[103,1384,1385],{"class":116}," &",[103,1387,1309],{"class":133},[103,1389,1390],{"class":126},"{Name: ",[103,1392,1393],{"class":189},"\"Alice\"",[103,1395,1396],{"class":126},"}, ",[103,1398,737],{"class":280},[103,1400,1402],{"class":105,"line":1401},31,[103,1403,146],{"class":126},[103,1405,1407],{"class":105,"line":1406},32,[103,1408,176],{"emptyLinePlaceholder":175},[103,1410,1412,1414,1416],{"class":105,"line":1411},33,[103,1413,223],{"class":116},[103,1415,226],{"class":133},[103,1417,229],{"class":126},[103,1419,1421,1424,1426,1428,1430,1433],{"class":105,"line":1420},34,[103,1422,1423],{"class":126},"    user, err ",[103,1425,244],{"class":116},[103,1427,1295],{"class":133},[103,1429,253],{"class":126},[103,1431,1432],{"class":280},"0",[103,1434,212],{"class":126},[103,1436,1438,1440,1442,1445],{"class":105,"line":1437},35,[103,1439,320],{"class":126},[103,1441,323],{"class":133},[103,1443,1444],{"class":126},"(user, err) ",[103,1446,1447],{"class":109},"\u002F\u002F \u003Cnil> database problem\n",[103,1449,1451],{"class":105,"line":1450},36,[103,1452,176],{"emptyLinePlaceholder":175},[103,1454,1456,1458,1460,1462,1464,1467],{"class":105,"line":1455},37,[103,1457,1423],{"class":126},[103,1459,409],{"class":116},[103,1461,1295],{"class":133},[103,1463,253],{"class":126},[103,1465,1466],{"class":280},"1",[103,1468,212],{"class":126},[103,1470,1472,1474,1476,1478],{"class":105,"line":1471},38,[103,1473,320],{"class":126},[103,1475,323],{"class":133},[103,1477,1444],{"class":126},[103,1479,1480],{"class":109},"\u002F\u002F &{Alice} \u003Cnil>\n",[103,1482,1484],{"class":105,"line":1483},39,[103,1485,146],{"class":126},[148,1487,1488,1491],{},[17,1489,1490],{},"sql.ErrNoRows"," — классический пример sentinel: нет строк, запрос пуст.",[90,1493,1495],{"id":1494},"проблема-мутабельность","Проблема: мутабельность",[95,1497,1499],{"className":97,"code":1498,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"io\"\n)\n\nfunc main() {\n    \u002F\u002F Кто угодно может поменять глобальную ошибку!\n    prevEOF := io.EOF\n    io.EOF = errors.New(\"hacked!\")\n\n    fmt.Println(io.EOF == prevEOF) \u002F\u002F false — сломали!\n}\n",[17,1500,1501,1507,1511,1517,1525,1533,1542,1546,1550,1558,1563,1573,1591,1595,1612],{"__ignoreMap":5},[103,1502,1503,1505],{"class":105,"line":106},[103,1504,167],{"class":116},[103,1506,170],{"class":133},[103,1508,1509],{"class":105,"line":113},[103,1510,176],{"emptyLinePlaceholder":175},[103,1512,1513,1515],{"class":105,"line":130},[103,1514,181],{"class":116},[103,1516,184],{"class":126},[103,1518,1519,1521,1523],{"class":105,"line":143},[103,1520,190],{"class":189},[103,1522,193],{"class":133},[103,1524,196],{"class":189},[103,1526,1527,1529,1531],{"class":105,"line":199},[103,1528,190],{"class":189},[103,1530,204],{"class":133},[103,1532,196],{"class":189},[103,1534,1535,1537,1540],{"class":105,"line":209},[103,1536,190],{"class":189},[103,1538,1539],{"class":133},"io",[103,1541,196],{"class":189},[103,1543,1544],{"class":105,"line":215},[103,1545,212],{"class":126},[103,1547,1548],{"class":105,"line":220},[103,1549,176],{"emptyLinePlaceholder":175},[103,1551,1552,1554,1556],{"class":105,"line":232},[103,1553,223],{"class":116},[103,1555,226],{"class":133},[103,1557,229],{"class":126},[103,1559,1560],{"class":105,"line":238},[103,1561,1562],{"class":109},"    \u002F\u002F Кто угодно может поменять глобальную ошибку!\n",[103,1564,1565,1568,1570],{"class":105,"line":261},[103,1566,1567],{"class":126},"    prevEOF ",[103,1569,244],{"class":116},[103,1571,1572],{"class":126}," io.EOF\n",[103,1574,1575,1578,1580,1582,1584,1586,1589],{"class":105,"line":266},[103,1576,1577],{"class":126},"    io.EOF ",[103,1579,409],{"class":116},[103,1581,247],{"class":126},[103,1583,250],{"class":133},[103,1585,253],{"class":126},[103,1587,1588],{"class":189},"\"hacked!\"",[103,1590,212],{"class":126},[103,1592,1593],{"class":105,"line":272},[103,1594,176],{"emptyLinePlaceholder":175},[103,1596,1597,1599,1601,1604,1606,1609],{"class":105,"line":284},[103,1598,320],{"class":126},[103,1600,323],{"class":133},[103,1602,1603],{"class":126},"(io.EOF ",[103,1605,686],{"class":116},[103,1607,1608],{"class":126}," prevEOF) ",[103,1610,1611],{"class":109},"\u002F\u002F false — сломали!\n",[103,1613,1614],{"class":105,"line":312},[103,1615,146],{"class":126},[148,1617,1618],{},"Никто так не делает, но технически возможно.",[90,1620,1622],{"id":1621},"решение-константные-ошибки","Решение: константные ошибки",[95,1624,1626],{"className":97,"code":1625,"language":99,"meta":5,"style":5},"package main\n\nimport \"fmt\"\n\ntype ConstError string\n\nfunc (e ConstError) Error() string {\n    return string(e) \u002F\u002F type definition → явное приведение\n}\n\nconst ErrDatabase ConstError = \"database problem\"\n\n\u002F\u002F Нельзя изменить:\n\u002F\u002F ErrDatabase = \"other\"  \u002F\u002F ❌ ошибка компиляции — константу менять нельзя\n\nfunc main() {\n    fmt.Println(ErrDatabase) \u002F\u002F database problem\n}\n",[17,1627,1628,1634,1638,1649,1653,1663,1667,1689,1701,1705,1709,1725,1729,1734,1742,1746,1754,1766],{"__ignoreMap":5},[103,1629,1630,1632],{"class":105,"line":106},[103,1631,167],{"class":116},[103,1633,170],{"class":133},[103,1635,1636],{"class":105,"line":113},[103,1637,176],{"emptyLinePlaceholder":175},[103,1639,1640,1642,1645,1647],{"class":105,"line":130},[103,1641,181],{"class":116},[103,1643,1644],{"class":189}," \"",[103,1646,204],{"class":133},[103,1648,196],{"class":189},[103,1650,1651],{"class":105,"line":143},[103,1652,176],{"emptyLinePlaceholder":175},[103,1654,1655,1657,1660],{"class":105,"line":199},[103,1656,117],{"class":116},[103,1658,1659],{"class":133}," ConstError",[103,1661,1662],{"class":116}," string\n",[103,1664,1665],{"class":105,"line":209},[103,1666,176],{"emptyLinePlaceholder":175},[103,1668,1669,1671,1673,1676,1679,1681,1683,1685,1687],{"class":105,"line":215},[103,1670,223],{"class":116},[103,1672,53],{"class":126},[103,1674,1675],{"class":658},"e ",[103,1677,1678],{"class":133},"ConstError",[103,1680,445],{"class":126},[103,1682,67],{"class":133},[103,1684,137],{"class":126},[103,1686,834],{"class":116},[103,1688,127],{"class":126},[103,1690,1691,1693,1695,1698],{"class":105,"line":220},[103,1692,721],{"class":116},[103,1694,662],{"class":116},[103,1696,1697],{"class":126},"(e) ",[103,1699,1700],{"class":109},"\u002F\u002F type definition → явное приведение\n",[103,1702,1703],{"class":105,"line":232},[103,1704,146],{"class":126},[103,1706,1707],{"class":105,"line":238},[103,1708,176],{"emptyLinePlaceholder":175},[103,1710,1711,1714,1717,1719,1722],{"class":105,"line":261},[103,1712,1713],{"class":116},"const",[103,1715,1716],{"class":280}," ErrDatabase",[103,1718,1659],{"class":133},[103,1720,1721],{"class":116}," =",[103,1723,1724],{"class":189}," \"database problem\"\n",[103,1726,1727],{"class":105,"line":266},[103,1728,176],{"emptyLinePlaceholder":175},[103,1730,1731],{"class":105,"line":272},[103,1732,1733],{"class":109},"\u002F\u002F Нельзя изменить:\n",[103,1735,1736,1739],{"class":105,"line":284},[103,1737,1738],{"class":109},"\u002F\u002F ErrDatabase = \"other\"",[103,1740,1741],{"class":109},"  \u002F\u002F ❌ ошибка компиляции — константу менять нельзя\n",[103,1743,1744],{"class":105,"line":312},[103,1745,176],{"emptyLinePlaceholder":175},[103,1747,1748,1750,1752],{"class":105,"line":317},[103,1749,223],{"class":116},[103,1751,226],{"class":133},[103,1753,229],{"class":126},[103,1755,1756,1758,1760,1763],{"class":105,"line":329},[103,1757,320],{"class":126},[103,1759,323],{"class":133},[103,1761,1762],{"class":126},"(ErrDatabase) ",[103,1764,1765],{"class":109},"\u002F\u002F database problem\n",[103,1767,1768],{"class":105,"line":339},[103,1769,146],{"class":126},[148,1771,1772],{},"Хак: type definition от string → можно сделать константой. Метод Error() делает совместимым с интерфейсом error.",[148,1774,1775,1776,1779,1780,1783],{},"Почему нужно явное приведение ",[17,1777,1778],{},"string(e)"," в методе Error(): type definition создаёт новый тип, не псевдоним, поэтому прямое использование ",[17,1781,1782],{},"e"," в строковом контексте запрещено.",[90,1785,1787],{"id":1786},"оборачивание-работает","Оборачивание работает",[95,1789,1791],{"className":97,"code":1790,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\ntype ConstError string\n\nfunc (e ConstError) Error() string { return string(e) }\n\nconst ErrDatabase ConstError = \"database problem\"\n\nfunc main() {\n    wrapped := fmt.Errorf(\"query failed: %w\", ErrDatabase)\n    fmt.Println(errors.Is(wrapped, ErrDatabase)) \u002F\u002F true\n}\n",[17,1792,1793,1799,1803,1809,1817,1825,1829,1833,1841,1845,1872,1876,1888,1892,1900,1925,1941],{"__ignoreMap":5},[103,1794,1795,1797],{"class":105,"line":106},[103,1796,167],{"class":116},[103,1798,170],{"class":133},[103,1800,1801],{"class":105,"line":113},[103,1802,176],{"emptyLinePlaceholder":175},[103,1804,1805,1807],{"class":105,"line":130},[103,1806,181],{"class":116},[103,1808,184],{"class":126},[103,1810,1811,1813,1815],{"class":105,"line":143},[103,1812,190],{"class":189},[103,1814,193],{"class":133},[103,1816,196],{"class":189},[103,1818,1819,1821,1823],{"class":105,"line":199},[103,1820,190],{"class":189},[103,1822,204],{"class":133},[103,1824,196],{"class":189},[103,1826,1827],{"class":105,"line":209},[103,1828,212],{"class":126},[103,1830,1831],{"class":105,"line":215},[103,1832,176],{"emptyLinePlaceholder":175},[103,1834,1835,1837,1839],{"class":105,"line":220},[103,1836,117],{"class":116},[103,1838,1659],{"class":133},[103,1840,1662],{"class":116},[103,1842,1843],{"class":105,"line":232},[103,1844,176],{"emptyLinePlaceholder":175},[103,1846,1847,1849,1851,1853,1855,1857,1859,1861,1863,1865,1867,1869],{"class":105,"line":238},[103,1848,223],{"class":116},[103,1850,53],{"class":126},[103,1852,1675],{"class":658},[103,1854,1678],{"class":133},[103,1856,445],{"class":126},[103,1858,67],{"class":133},[103,1860,137],{"class":126},[103,1862,834],{"class":116},[103,1864,904],{"class":126},[103,1866,907],{"class":116},[103,1868,662],{"class":116},[103,1870,1871],{"class":126},"(e) }\n",[103,1873,1874],{"class":105,"line":261},[103,1875,176],{"emptyLinePlaceholder":175},[103,1877,1878,1880,1882,1884,1886],{"class":105,"line":266},[103,1879,1713],{"class":116},[103,1881,1716],{"class":280},[103,1883,1659],{"class":133},[103,1885,1721],{"class":116},[103,1887,1724],{"class":189},[103,1889,1890],{"class":105,"line":272},[103,1891,176],{"emptyLinePlaceholder":175},[103,1893,1894,1896,1898],{"class":105,"line":284},[103,1895,223],{"class":116},[103,1897,226],{"class":133},[103,1899,229],{"class":126},[103,1901,1902,1905,1907,1909,1911,1913,1916,1919,1922],{"class":105,"line":312},[103,1903,1904],{"class":126},"    wrapped ",[103,1906,244],{"class":116},[103,1908,292],{"class":126},[103,1910,295],{"class":133},[103,1912,253],{"class":126},[103,1914,1915],{"class":189},"\"query failed: ",[103,1917,1918],{"class":280},"%w",[103,1920,1921],{"class":189},"\"",[103,1923,1924],{"class":126},", ErrDatabase)\n",[103,1926,1927,1929,1931,1933,1935,1938],{"class":105,"line":317},[103,1928,320],{"class":126},[103,1930,323],{"class":133},[103,1932,555],{"class":126},[103,1934,1335],{"class":133},[103,1936,1937],{"class":126},"(wrapped, ErrDatabase)) ",[103,1939,1940],{"class":109},"\u002F\u002F true\n",[103,1942,1943],{"class":105,"line":329},[103,1944,146],{"class":126},[148,1946,1947],{},"ConstError — обычная ошибка, errors.Is\u002FAs\u002FUnwrap работают.",[90,1949,1951],{"id":1950},"почему-не-сравнивать-через","Почему не сравнивать через ==",[148,1953,1954,1955,1958,1959,1962,1963,1965],{},"Если ошибка была обёрнута через ",[17,1956,1957],{},"fmt.Errorf(\"%w\", sentinel)",", прямое сравнение ",[17,1960,1961],{},"err == sentinel"," вернёт false. ",[17,1964,1094],{}," раскручивает цепочку обёрток.",[82,1967],{},[11,1969,1970,1973,1983,1992,1998],{},[14,1971,1972],{},"оборачивание = упаковка ошибки в контейнер-обёртку, сохраняя доступ к исходной",[14,1974,1975,1978,1979,1982],{},[17,1976,1977],{},"%v"," — просто вставляет текст, Unwrap ",[39,1980,1981],{},"не работает"," (нет метода Unwrap)",[14,1984,1985,1987,1988,1991],{},[17,1986,1918],{}," (Go 1.13+) — создаёт тип с методом ",[17,1989,1990],{},"Unwrap()",", цепочка разворачивания работает",[14,1993,1994,1997],{},[17,1995,1996],{},"errors.Unwrap()"," — вызывает метод Unwrap, возвращает внутреннюю ошибку (или nil)",[14,1999,2000,2001,2004],{},"зачем: добавить контекст при пробросе ",[39,2002,2003],{},"или"," пометить ошибку sentinel'ом",[82,2006],{},[85,2008,2010],{"id":2009},"оборачивание-и-цепочки-ошибок","Оборачивание и цепочки ошибок",[90,2012,2014],{"id":2013},"v-vs-w","%v vs %w",[95,2016,2018],{"className":97,"code":2017,"language":99,"meta":5,"style":5},"original := errors.New(\"connection refused\")\n\n\u002F\u002F %v — просто текст, unwrap НЕ работает\n\u002F\u002F (такая же структра создается и для errors.New())\n\u002F\u002Ftype errorString struct {\n\u002F\u002F    s string\n\u002F\u002F} \n\nwrapped1 := fmt.Errorf(\"db error: %v\", original)\nfmt.Println(errors.Unwrap(wrapped1))  \u002F\u002F \u003Cnil> — нечего разворачивать\n\n\u002F\u002F %w — создаёт обёртку с Unwrap(), цепочка работает\n\u002F\u002Ftype wrapError struct {\n\u002F\u002F    msg   string\n\u002F\u002F    err   error \u002F\u002F Здесь хранится ссылка на оригинальную ошибку\n\u002F\u002F}\n\nwrapped2 := fmt.Errorf(\"db error: %w\", original)\nfmt.Println(errors.Unwrap(wrapped2))  \u002F\u002F \"connection refused\"\n",[17,2019,2020,2037,2041,2046,2051,2056,2061,2066,2070,2093,2111,2115,2120,2125,2130,2138,2143,2147,2168],{"__ignoreMap":5},[103,2021,2022,2025,2027,2029,2031,2033,2035],{"class":105,"line":106},[103,2023,2024],{"class":126},"original ",[103,2026,244],{"class":116},[103,2028,247],{"class":126},[103,2030,250],{"class":133},[103,2032,253],{"class":126},[103,2034,256],{"class":189},[103,2036,212],{"class":126},[103,2038,2039],{"class":105,"line":113},[103,2040,176],{"emptyLinePlaceholder":175},[103,2042,2043],{"class":105,"line":130},[103,2044,2045],{"class":109},"\u002F\u002F %v — просто текст, unwrap НЕ работает\n",[103,2047,2048],{"class":105,"line":143},[103,2049,2050],{"class":109},"\u002F\u002F (такая же структра создается и для errors.New())\n",[103,2052,2053],{"class":105,"line":199},[103,2054,2055],{"class":109},"\u002F\u002Ftype errorString struct {\n",[103,2057,2058],{"class":105,"line":209},[103,2059,2060],{"class":109},"\u002F\u002F    s string\n",[103,2062,2063],{"class":105,"line":215},[103,2064,2065],{"class":109},"\u002F\u002F} \n",[103,2067,2068],{"class":105,"line":220},[103,2069,176],{"emptyLinePlaceholder":175},[103,2071,2072,2075,2077,2079,2081,2083,2086,2088,2090],{"class":105,"line":232},[103,2073,2074],{"class":126},"wrapped1 ",[103,2076,244],{"class":116},[103,2078,292],{"class":126},[103,2080,295],{"class":133},[103,2082,253],{"class":126},[103,2084,2085],{"class":189},"\"db error: ",[103,2087,1977],{"class":280},[103,2089,1921],{"class":189},[103,2091,2092],{"class":126},", original)\n",[103,2094,2095,2098,2100,2102,2105,2108],{"class":105,"line":238},[103,2096,2097],{"class":126},"fmt.",[103,2099,323],{"class":133},[103,2101,555],{"class":126},[103,2103,2104],{"class":133},"Unwrap",[103,2106,2107],{"class":126},"(wrapped1))  ",[103,2109,2110],{"class":109},"\u002F\u002F \u003Cnil> — нечего разворачивать\n",[103,2112,2113],{"class":105,"line":261},[103,2114,176],{"emptyLinePlaceholder":175},[103,2116,2117],{"class":105,"line":266},[103,2118,2119],{"class":109},"\u002F\u002F %w — создаёт обёртку с Unwrap(), цепочка работает\n",[103,2121,2122],{"class":105,"line":272},[103,2123,2124],{"class":109},"\u002F\u002Ftype wrapError struct {\n",[103,2126,2127],{"class":105,"line":284},[103,2128,2129],{"class":109},"\u002F\u002F    msg   string\n",[103,2131,2132,2135],{"class":105,"line":312},[103,2133,2134],{"class":109},"\u002F\u002F    err   error",[103,2136,2137],{"class":109}," \u002F\u002F Здесь хранится ссылка на оригинальную ошибку\n",[103,2139,2140],{"class":105,"line":317},[103,2141,2142],{"class":109},"\u002F\u002F}\n",[103,2144,2145],{"class":105,"line":329},[103,2146,176],{"emptyLinePlaceholder":175},[103,2148,2149,2152,2154,2156,2158,2160,2162,2164,2166],{"class":105,"line":339},[103,2150,2151],{"class":126},"wrapped2 ",[103,2153,244],{"class":116},[103,2155,292],{"class":126},[103,2157,295],{"class":133},[103,2159,253],{"class":126},[103,2161,2085],{"class":189},[103,2163,1918],{"class":280},[103,2165,1921],{"class":189},[103,2167,2092],{"class":126},[103,2169,2170,2172,2174,2176,2178,2181],{"class":105,"line":506},[103,2171,2097],{"class":126},[103,2173,323],{"class":133},[103,2175,555],{"class":126},[103,2177,2104],{"class":133},[103,2179,2180],{"class":126},"(wrapped2))  ",[103,2182,2183],{"class":109},"\u002F\u002F \"connection refused\"\n",[148,2185,2186,2188,2189,2191,2192,2195,2196,2199,2200,2202,2203,2205],{},[17,2187,1977],{}," создаёт строку без метода ",[17,2190,2104],{}," — ",[17,2193,2194],{},"errors.Unwrap"," возвращает ",[17,2197,2198],{},"nil",". ",[17,2201,1918],{}," создаёт специальный тип с методом ",[17,2204,1990],{},", который возвращает оригинальную ошибку.",[90,2207,2209],{"id":2208},"цепочка-оборачивания","Цепочка оборачивания",[95,2211,2213],{"className":97,"code":2212,"language":99,"meta":5,"style":5},"err1 := errors.New(\"disk full\")\nerr2 := fmt.Errorf(\"write failed: %w\", err1)\nerr3 := fmt.Errorf(\"save config: %w\", err2)\n\n\u002F\u002F Разворачиваем по одному:\nfmt.Println(err3)                      \u002F\u002F \"save config: write failed: disk full\"\nfmt.Println(errors.Unwrap(err3))       \u002F\u002F \"write failed: disk full\"\nfmt.Println(errors.Unwrap(errors.Unwrap(err3)))  \u002F\u002F \"disk full\"\nfmt.Println(errors.Unwrap(errors.Unwrap(errors.Unwrap(err3))))  \u002F\u002F \u003Cnil>\n",[17,2214,2215,2233,2256,2279,2283,2288,2300,2316,2336],{"__ignoreMap":5},[103,2216,2217,2220,2222,2224,2226,2228,2231],{"class":105,"line":106},[103,2218,2219],{"class":126},"err1 ",[103,2221,244],{"class":116},[103,2223,247],{"class":126},[103,2225,250],{"class":133},[103,2227,253],{"class":126},[103,2229,2230],{"class":189},"\"disk full\"",[103,2232,212],{"class":126},[103,2234,2235,2238,2240,2242,2244,2246,2249,2251,2253],{"class":105,"line":113},[103,2236,2237],{"class":126},"err2 ",[103,2239,244],{"class":116},[103,2241,292],{"class":126},[103,2243,295],{"class":133},[103,2245,253],{"class":126},[103,2247,2248],{"class":189},"\"write failed: ",[103,2250,1918],{"class":280},[103,2252,1921],{"class":189},[103,2254,2255],{"class":126},", err1)\n",[103,2257,2258,2261,2263,2265,2267,2269,2272,2274,2276],{"class":105,"line":130},[103,2259,2260],{"class":126},"err3 ",[103,2262,244],{"class":116},[103,2264,292],{"class":126},[103,2266,295],{"class":133},[103,2268,253],{"class":126},[103,2270,2271],{"class":189},"\"save config: ",[103,2273,1918],{"class":280},[103,2275,1921],{"class":189},[103,2277,2278],{"class":126},", err2)\n",[103,2280,2281],{"class":105,"line":143},[103,2282,176],{"emptyLinePlaceholder":175},[103,2284,2285],{"class":105,"line":199},[103,2286,2287],{"class":109},"\u002F\u002F Разворачиваем по одному:\n",[103,2289,2290,2292,2294,2297],{"class":105,"line":209},[103,2291,2097],{"class":126},[103,2293,323],{"class":133},[103,2295,2296],{"class":126},"(err3)                      ",[103,2298,2299],{"class":109},"\u002F\u002F \"save config: write failed: disk full\"\n",[103,2301,2302,2304,2306,2308,2310,2313],{"class":105,"line":215},[103,2303,2097],{"class":126},[103,2305,323],{"class":133},[103,2307,555],{"class":126},[103,2309,2104],{"class":133},[103,2311,2312],{"class":126},"(err3))       ",[103,2314,2315],{"class":109},"\u002F\u002F \"write failed: disk full\"\n",[103,2317,2318,2320,2322,2324,2326,2328,2330,2333],{"class":105,"line":220},[103,2319,2097],{"class":126},[103,2321,323],{"class":133},[103,2323,555],{"class":126},[103,2325,2104],{"class":133},[103,2327,555],{"class":126},[103,2329,2104],{"class":133},[103,2331,2332],{"class":126},"(err3)))  ",[103,2334,2335],{"class":109},"\u002F\u002F \"disk full\"\n",[103,2337,2338,2340,2342,2344,2346,2348,2350,2352,2354,2357],{"class":105,"line":232},[103,2339,2097],{"class":126},[103,2341,323],{"class":133},[103,2343,555],{"class":126},[103,2345,2104],{"class":133},[103,2347,555],{"class":126},[103,2349,2104],{"class":133},[103,2351,555],{"class":126},[103,2353,2104],{"class":133},[103,2355,2356],{"class":126},"(err3))))  ",[103,2358,2359],{"class":109},"\u002F\u002F \u003Cnil>\n",[148,2361,2362,2363,2365,2366,2368],{},"Каждый ",[17,2364,1918],{}," добавляет слой. Unwrap снимает по одному. Финальный Unwrap на самой глубокой ошибке возвращает ",[17,2367,2198],{},".",[90,2370,2372],{"id":2371},"два-сценария-использования","Два сценария использования",[95,2374,2376],{"className":97,"code":2375,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"os\"\n)\n\n\u002F\u002F 1. Добавить контекст при пробросе\nfunc ReadConfig(path string) error {\n    _, err := os.ReadFile(path)\n    if err != nil {\n        return fmt.Errorf(\"reading config %s: %w\", path, err)\n    }\n    return nil\n}\n\n\u002F\u002F 2. Пометить sentinel'ом\nvar ErrValidation = errors.New(\"validation error\")\n\nfunc Validate(input string) error {\n    if input == \"\" {\n        return fmt.Errorf(\"%w: empty input\", ErrValidation)\n    }\n    return nil\n}\n\nfunc main() {\n    fmt.Println(ReadConfig(\"\u002Fnonexistent\u002Fconfig.yaml\"))\n    err := Validate(\"\")\n    fmt.Println(errors.Is(err, ErrValidation)) \u002F\u002F true\n}\n",[17,2377,2378,2384,2388,2394,2402,2410,2419,2423,2427,2432,2451,2467,2479,2505,2509,2515,2519,2523,2528,2548,2552,2572,2585,2605,2609,2615,2619,2623,2631,2650,2665,2680],{"__ignoreMap":5},[103,2379,2380,2382],{"class":105,"line":106},[103,2381,167],{"class":116},[103,2383,170],{"class":133},[103,2385,2386],{"class":105,"line":113},[103,2387,176],{"emptyLinePlaceholder":175},[103,2389,2390,2392],{"class":105,"line":130},[103,2391,181],{"class":116},[103,2393,184],{"class":126},[103,2395,2396,2398,2400],{"class":105,"line":143},[103,2397,190],{"class":189},[103,2399,193],{"class":133},[103,2401,196],{"class":189},[103,2403,2404,2406,2408],{"class":105,"line":199},[103,2405,190],{"class":189},[103,2407,204],{"class":133},[103,2409,196],{"class":189},[103,2411,2412,2414,2417],{"class":105,"line":209},[103,2413,190],{"class":189},[103,2415,2416],{"class":133},"os",[103,2418,196],{"class":189},[103,2420,2421],{"class":105,"line":215},[103,2422,212],{"class":126},[103,2424,2425],{"class":105,"line":220},[103,2426,176],{"emptyLinePlaceholder":175},[103,2428,2429],{"class":105,"line":232},[103,2430,2431],{"class":109},"\u002F\u002F 1. Добавить контекст при пробросе\n",[103,2433,2434,2436,2439,2441,2443,2445,2447,2449],{"class":105,"line":238},[103,2435,223],{"class":116},[103,2437,2438],{"class":133}," ReadConfig",[103,2440,253],{"class":126},[103,2442,659],{"class":658},[103,2444,662],{"class":116},[103,2446,445],{"class":126},[103,2448,19],{"class":116},[103,2450,127],{"class":126},[103,2452,2453,2456,2458,2461,2464],{"class":105,"line":261},[103,2454,2455],{"class":126},"    _, err ",[103,2457,244],{"class":116},[103,2459,2460],{"class":126}," os.",[103,2462,2463],{"class":133},"ReadFile",[103,2465,2466],{"class":126},"(path)\n",[103,2468,2469,2471,2473,2475,2477],{"class":105,"line":266},[103,2470,680],{"class":116},[103,2472,960],{"class":126},[103,2474,963],{"class":116},[103,2476,699],{"class":280},[103,2478,127],{"class":126},[103,2480,2481,2483,2485,2487,2489,2492,2495,2498,2500,2502],{"class":105,"line":272},[103,2482,696],{"class":116},[103,2484,292],{"class":126},[103,2486,295],{"class":133},[103,2488,253],{"class":126},[103,2490,2491],{"class":189},"\"reading config ",[103,2493,2494],{"class":280},"%s",[103,2496,2497],{"class":189},": ",[103,2499,1918],{"class":280},[103,2501,1921],{"class":189},[103,2503,2504],{"class":126},", path, err)\n",[103,2506,2507],{"class":105,"line":284},[103,2508,716],{"class":126},[103,2510,2511,2513],{"class":105,"line":312},[103,2512,721],{"class":116},[103,2514,985],{"class":280},[103,2516,2517],{"class":105,"line":317},[103,2518,146],{"class":126},[103,2520,2521],{"class":105,"line":329},[103,2522,176],{"emptyLinePlaceholder":175},[103,2524,2525],{"class":105,"line":339},[103,2526,2527],{"class":109},"\u002F\u002F 2. Пометить sentinel'ом\n",[103,2529,2530,2532,2535,2537,2539,2541,2543,2546],{"class":105,"line":506},[103,2531,403],{"class":116},[103,2533,2534],{"class":126}," ErrValidation ",[103,2536,409],{"class":116},[103,2538,247],{"class":126},[103,2540,250],{"class":133},[103,2542,253],{"class":126},[103,2544,2545],{"class":189},"\"validation error\"",[103,2547,212],{"class":126},[103,2549,2550],{"class":105,"line":514},[103,2551,176],{"emptyLinePlaceholder":175},[103,2553,2554,2556,2559,2561,2564,2566,2568,2570],{"class":105,"line":523},[103,2555,223],{"class":116},[103,2557,2558],{"class":133}," Validate",[103,2560,253],{"class":126},[103,2562,2563],{"class":658},"input",[103,2565,662],{"class":116},[103,2567,445],{"class":126},[103,2569,19],{"class":116},[103,2571,127],{"class":126},[103,2573,2574,2576,2579,2581,2583],{"class":105,"line":528},[103,2575,680],{"class":116},[103,2577,2578],{"class":126}," input ",[103,2580,686],{"class":116},[103,2582,689],{"class":189},[103,2584,127],{"class":126},[103,2586,2587,2589,2591,2593,2595,2597,2599,2602],{"class":105,"line":533},[103,2588,696],{"class":116},[103,2590,292],{"class":126},[103,2592,295],{"class":133},[103,2594,253],{"class":126},[103,2596,1921],{"class":189},[103,2598,1918],{"class":280},[103,2600,2601],{"class":189},": empty input\"",[103,2603,2604],{"class":126},", ErrValidation)\n",[103,2606,2607],{"class":105,"line":542},[103,2608,716],{"class":126},[103,2610,2611,2613],{"class":105,"line":548},[103,2612,721],{"class":116},[103,2614,985],{"class":280},[103,2616,2617],{"class":105,"line":570},[103,2618,146],{"class":126},[103,2620,2621],{"class":105,"line":580},[103,2622,176],{"emptyLinePlaceholder":175},[103,2624,2625,2627,2629],{"class":105,"line":590},[103,2626,223],{"class":116},[103,2628,226],{"class":133},[103,2630,229],{"class":126},[103,2632,2633,2635,2637,2639,2642,2644,2647],{"class":105,"line":1375},[103,2634,320],{"class":126},[103,2636,323],{"class":133},[103,2638,253],{"class":126},[103,2640,2641],{"class":133},"ReadConfig",[103,2643,253],{"class":126},[103,2645,2646],{"class":189},"\"\u002Fnonexistent\u002Fconfig.yaml\"",[103,2648,2649],{"class":126},"))\n",[103,2651,2652,2654,2656,2658,2660,2663],{"class":105,"line":1380},[103,2653,946],{"class":126},[103,2655,244],{"class":116},[103,2657,2558],{"class":133},[103,2659,253],{"class":126},[103,2661,2662],{"class":189},"\"\"",[103,2664,212],{"class":126},[103,2666,2667,2669,2671,2673,2675,2678],{"class":105,"line":1401},[103,2668,320],{"class":126},[103,2670,323],{"class":133},[103,2672,555],{"class":126},[103,2674,1335],{"class":133},[103,2676,2677],{"class":126},"(err, ErrValidation)) ",[103,2679,1940],{"class":109},[103,2681,2682],{"class":105,"line":1406},[103,2683,146],{"class":126},[148,2685,2686,2687,2689],{},"Сценарий 1: ",[17,2688,1918],{}," добавляет контекст — теперь ошибка несёт информацию о пути и операции.",[148,2691,2692,2693,2696],{},"Сценарий 2: оборачивание sentinel'а — ",[17,2694,2695],{},"errors.Is(err, ErrValidation)"," будет работать, потому что цепочка разворачивается рекурсивно.",[90,2698,2700],{"id":2699},"ловушки","Ловушки",[95,2702,2704],{"className":97,"code":2703,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc main() {\n    err1 := errors.New(\"first error\")\n    err2 := errors.New(\"second error\")\n\n    \u002F\u002F Два %w — Go 1.20+ поддерживает multiple wrapping, но осторожно\n    wrapped := fmt.Errorf(\"%w and %w\", err1, err2)\n    fmt.Println(errors.Is(wrapped, err1)) \u002F\u002F true\n    fmt.Println(errors.Is(wrapped, err2)) \u002F\u002F true\n}\n",[17,2705,2706,2712,2716,2722,2730,2738,2742,2746,2754,2771,2788,2792,2797,2823,2838,2853],{"__ignoreMap":5},[103,2707,2708,2710],{"class":105,"line":106},[103,2709,167],{"class":116},[103,2711,170],{"class":133},[103,2713,2714],{"class":105,"line":113},[103,2715,176],{"emptyLinePlaceholder":175},[103,2717,2718,2720],{"class":105,"line":130},[103,2719,181],{"class":116},[103,2721,184],{"class":126},[103,2723,2724,2726,2728],{"class":105,"line":143},[103,2725,190],{"class":189},[103,2727,193],{"class":133},[103,2729,196],{"class":189},[103,2731,2732,2734,2736],{"class":105,"line":199},[103,2733,190],{"class":189},[103,2735,204],{"class":133},[103,2737,196],{"class":189},[103,2739,2740],{"class":105,"line":209},[103,2741,212],{"class":126},[103,2743,2744],{"class":105,"line":215},[103,2745,176],{"emptyLinePlaceholder":175},[103,2747,2748,2750,2752],{"class":105,"line":220},[103,2749,223],{"class":116},[103,2751,226],{"class":133},[103,2753,229],{"class":126},[103,2755,2756,2758,2760,2762,2764,2766,2769],{"class":105,"line":232},[103,2757,241],{"class":126},[103,2759,244],{"class":116},[103,2761,247],{"class":126},[103,2763,250],{"class":133},[103,2765,253],{"class":126},[103,2767,2768],{"class":189},"\"first error\"",[103,2770,212],{"class":126},[103,2772,2773,2775,2777,2779,2781,2783,2786],{"class":105,"line":238},[103,2774,287],{"class":126},[103,2776,244],{"class":116},[103,2778,247],{"class":126},[103,2780,250],{"class":133},[103,2782,253],{"class":126},[103,2784,2785],{"class":189},"\"second error\"",[103,2787,212],{"class":126},[103,2789,2790],{"class":105,"line":261},[103,2791,176],{"emptyLinePlaceholder":175},[103,2793,2794],{"class":105,"line":266},[103,2795,2796],{"class":109},"    \u002F\u002F Два %w — Go 1.20+ поддерживает multiple wrapping, но осторожно\n",[103,2798,2799,2801,2803,2805,2807,2809,2811,2813,2816,2818,2820],{"class":105,"line":272},[103,2800,1904],{"class":126},[103,2802,244],{"class":116},[103,2804,292],{"class":126},[103,2806,295],{"class":133},[103,2808,253],{"class":126},[103,2810,1921],{"class":189},[103,2812,1918],{"class":280},[103,2814,2815],{"class":189}," and ",[103,2817,1918],{"class":280},[103,2819,1921],{"class":189},[103,2821,2822],{"class":126},", err1, err2)\n",[103,2824,2825,2827,2829,2831,2833,2836],{"class":105,"line":284},[103,2826,320],{"class":126},[103,2828,323],{"class":133},[103,2830,555],{"class":126},[103,2832,1335],{"class":133},[103,2834,2835],{"class":126},"(wrapped, err1)) ",[103,2837,1940],{"class":109},[103,2839,2840,2842,2844,2846,2848,2851],{"class":105,"line":312},[103,2841,320],{"class":126},[103,2843,323],{"class":133},[103,2845,555],{"class":126},[103,2847,1335],{"class":133},[103,2849,2850],{"class":126},"(wrapped, err2)) ",[103,2852,1940],{"class":109},[103,2854,2855],{"class":105,"line":317},[103,2856,146],{"class":126},[148,2858,2859,2860,2862,2863,2866,2867,2869],{},"Go 1.20+ поддерживает несколько ",[17,2861,1918],{}," в одном ",[17,2864,2865],{},"fmt.Errorf"," — создаётся ошибка с несколькими обёрнутыми ошибками (multiple wrapping). До 1.20 поведение с двумя ",[17,2868,1918],{}," было undefined.",[82,2871],{},[11,2873,2874,2883,2892,2898,2905],{},[14,2875,2876,2878,2879,2882],{},[17,2877,1094],{}," — рекурсивный Unwrap + сравнение по ",[39,2880,2881],{},"значению"," (для sentinel)",[14,2884,2885,2878,2888,2891],{},[17,2886,2887],{},"errors.As",[39,2889,2890],{},"типу"," (для кастомных типов)",[14,2893,2894,2897],{},[39,2895,2896],{},"всегда"," использовать Is\u002FAs вместо == и type switch — код меняется, обёртки добавляются",[14,2899,2900,2901,2904],{},"type switch по обёрнутой ошибке ",[39,2902,2903],{},"не найдёт"," исходный тип (за интерфейсом будет тип обёртки)",[14,2906,2907,2908,2911],{},"специальный приём: оборачивание sentinel без контекста — чтобы ",[39,2909,2910],{},"запретить"," ==",[82,2913],{},[85,2915,2917],{"id":2916},"errorsis-errorsas","errors.Is \u002F errors.As",[90,2919,2921],{"id":2920},"errorsis-сравнение-по-значению","errors.Is — сравнение по значению",[95,2923,2925],{"className":97,"code":2924,"language":99,"meta":5,"style":5},"var ErrDB = errors.New(\"database problem\")\n\nwrapped := fmt.Errorf(\"query: %w\", ErrDB)\n\n\u002F\u002F ❌ не сработает — wrapped != ErrDB (разные объекты)\nfmt.Println(wrapped == ErrDB)           \u002F\u002F false\n\n\u002F\u002F ✅ рекурсивно unwrap'ит и находит ErrDB внутри\nfmt.Println(errors.Is(wrapped, ErrDB))  \u002F\u002F true\n",[17,2926,2927,2946,2950,2973,2977,2982,2999,3003,3008],{"__ignoreMap":5},[103,2928,2929,2931,2934,2936,2938,2940,2942,2944],{"class":105,"line":106},[103,2930,403],{"class":116},[103,2932,2933],{"class":126}," ErrDB ",[103,2935,409],{"class":116},[103,2937,247],{"class":126},[103,2939,250],{"class":133},[103,2941,253],{"class":126},[103,2943,1196],{"class":189},[103,2945,212],{"class":126},[103,2947,2948],{"class":105,"line":113},[103,2949,176],{"emptyLinePlaceholder":175},[103,2951,2952,2955,2957,2959,2961,2963,2966,2968,2970],{"class":105,"line":130},[103,2953,2954],{"class":126},"wrapped ",[103,2956,244],{"class":116},[103,2958,292],{"class":126},[103,2960,295],{"class":133},[103,2962,253],{"class":126},[103,2964,2965],{"class":189},"\"query: ",[103,2967,1918],{"class":280},[103,2969,1921],{"class":189},[103,2971,2972],{"class":126},", ErrDB)\n",[103,2974,2975],{"class":105,"line":143},[103,2976,176],{"emptyLinePlaceholder":175},[103,2978,2979],{"class":105,"line":199},[103,2980,2981],{"class":109},"\u002F\u002F ❌ не сработает — wrapped != ErrDB (разные объекты)\n",[103,2983,2984,2986,2988,2991,2993,2996],{"class":105,"line":209},[103,2985,2097],{"class":126},[103,2987,323],{"class":133},[103,2989,2990],{"class":126},"(wrapped ",[103,2992,686],{"class":116},[103,2994,2995],{"class":126}," ErrDB)           ",[103,2997,2998],{"class":109},"\u002F\u002F false\n",[103,3000,3001],{"class":105,"line":215},[103,3002,176],{"emptyLinePlaceholder":175},[103,3004,3005],{"class":105,"line":220},[103,3006,3007],{"class":109},"\u002F\u002F ✅ рекурсивно unwrap'ит и находит ErrDB внутри\n",[103,3009,3010,3012,3014,3016,3018,3021],{"class":105,"line":232},[103,3011,2097],{"class":126},[103,3013,323],{"class":133},[103,3015,555],{"class":126},[103,3017,1335],{"class":133},[103,3019,3020],{"class":126},"(wrapped, ErrDB))  ",[103,3022,1940],{"class":109},[148,3024,3025,3027,3028,3030],{},[17,3026,1094],{}," рекурсивно вызывает ",[17,3029,1990],{}," на каждом уровне цепочки, пока не найдёт совпадение по значению или не исчерпает цепочку.",[90,3032,3034],{"id":3033},"errorsas-сравнение-по-типу","errors.As — сравнение по типу",[95,3036,3038],{"className":97,"code":3037,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\ntype DatabaseError struct {\n    Query string\n    Err   error\n}\n\nfunc (e *DatabaseError) Error() string { return e.Err.Error() }\n\nfunc main() {\n    original := &DatabaseError{Query: \"SELECT\", Err: errors.New(\"timeout\")}\n    wrapped := fmt.Errorf(\"handler: %w\", original)\n\n    \u002F\u002F ❌ type switch не найдёт — за интерфейсом тип обёртки, не DatabaseError\n    switch wrapped.(type) {\n    case *DatabaseError: \u002F\u002F сюда НЕ зайдёт\n    }\n\n    \u002F\u002F ✅ errors.As рекурсивно ищет нужный тип\n    var dbErr *DatabaseError\n    if errors.As(wrapped, &dbErr) {\n        fmt.Println(dbErr.Query) \u002F\u002F \"SELECT\"\n    }\n}\n",[17,3039,3040,3046,3050,3056,3064,3072,3076,3080,3091,3098,3105,3109,3113,3146,3150,3158,3188,3209,3213,3218,3230,3245,3249,3253,3258,3271,3289,3302,3306],{"__ignoreMap":5},[103,3041,3042,3044],{"class":105,"line":106},[103,3043,167],{"class":116},[103,3045,170],{"class":133},[103,3047,3048],{"class":105,"line":113},[103,3049,176],{"emptyLinePlaceholder":175},[103,3051,3052,3054],{"class":105,"line":130},[103,3053,181],{"class":116},[103,3055,184],{"class":126},[103,3057,3058,3060,3062],{"class":105,"line":143},[103,3059,190],{"class":189},[103,3061,193],{"class":133},[103,3063,196],{"class":189},[103,3065,3066,3068,3070],{"class":105,"line":199},[103,3067,190],{"class":189},[103,3069,204],{"class":133},[103,3071,196],{"class":189},[103,3073,3074],{"class":105,"line":209},[103,3075,212],{"class":126},[103,3077,3078],{"class":105,"line":215},[103,3079,176],{"emptyLinePlaceholder":175},[103,3081,3082,3084,3087,3089],{"class":105,"line":220},[103,3083,117],{"class":116},[103,3085,3086],{"class":133}," DatabaseError",[103,3088,467],{"class":116},[103,3090,127],{"class":126},[103,3092,3093,3096],{"class":105,"line":232},[103,3094,3095],{"class":126},"    Query ",[103,3097,140],{"class":116},[103,3099,3100,3103],{"class":105,"line":238},[103,3101,3102],{"class":126},"    Err   ",[103,3104,484],{"class":116},[103,3106,3107],{"class":105,"line":261},[103,3108,146],{"class":126},[103,3110,3111],{"class":105,"line":266},[103,3112,176],{"emptyLinePlaceholder":175},[103,3114,3115,3117,3119,3121,3123,3126,3128,3130,3132,3134,3136,3138,3141,3143],{"class":105,"line":272},[103,3116,223],{"class":116},[103,3118,53],{"class":126},[103,3120,1675],{"class":658},[103,3122,1306],{"class":116},[103,3124,3125],{"class":133},"DatabaseError",[103,3127,445],{"class":126},[103,3129,67],{"class":133},[103,3131,137],{"class":126},[103,3133,834],{"class":116},[103,3135,904],{"class":126},[103,3137,907],{"class":116},[103,3139,3140],{"class":126}," e.Err.",[103,3142,67],{"class":133},[103,3144,3145],{"class":126},"() }\n",[103,3147,3148],{"class":105,"line":284},[103,3149,176],{"emptyLinePlaceholder":175},[103,3151,3152,3154,3156],{"class":105,"line":312},[103,3153,223],{"class":116},[103,3155,226],{"class":133},[103,3157,229],{"class":126},[103,3159,3160,3163,3165,3167,3169,3172,3175,3178,3180,3182,3185],{"class":105,"line":317},[103,3161,3162],{"class":126},"    original ",[103,3164,244],{"class":116},[103,3166,1385],{"class":116},[103,3168,3125],{"class":133},[103,3170,3171],{"class":126},"{Query: ",[103,3173,3174],{"class":189},"\"SELECT\"",[103,3176,3177],{"class":126},", Err: errors.",[103,3179,250],{"class":133},[103,3181,253],{"class":126},[103,3183,3184],{"class":189},"\"timeout\"",[103,3186,3187],{"class":126},")}\n",[103,3189,3190,3192,3194,3196,3198,3200,3203,3205,3207],{"class":105,"line":329},[103,3191,1904],{"class":126},[103,3193,244],{"class":116},[103,3195,292],{"class":126},[103,3197,295],{"class":133},[103,3199,253],{"class":126},[103,3201,3202],{"class":189},"\"handler: ",[103,3204,1918],{"class":280},[103,3206,1921],{"class":189},[103,3208,2092],{"class":126},[103,3210,3211],{"class":105,"line":339},[103,3212,176],{"emptyLinePlaceholder":175},[103,3214,3215],{"class":105,"line":506},[103,3216,3217],{"class":109},"    \u002F\u002F ❌ type switch не найдёт — за интерфейсом тип обёртки, не DatabaseError\n",[103,3219,3220,3223,3226,3228],{"class":105,"line":514},[103,3221,3222],{"class":116},"    switch",[103,3224,3225],{"class":126}," wrapped.(",[103,3227,117],{"class":116},[103,3229,675],{"class":126},[103,3231,3232,3235,3238,3240,3242],{"class":105,"line":523},[103,3233,3234],{"class":116},"    case",[103,3236,3237],{"class":116}," *",[103,3239,3125],{"class":133},[103,3241,2497],{"class":126},[103,3243,3244],{"class":109},"\u002F\u002F сюда НЕ зайдёт\n",[103,3246,3247],{"class":105,"line":528},[103,3248,716],{"class":126},[103,3250,3251],{"class":105,"line":533},[103,3252,176],{"emptyLinePlaceholder":175},[103,3254,3255],{"class":105,"line":542},[103,3256,3257],{"class":109},"    \u002F\u002F ✅ errors.As рекурсивно ищет нужный тип\n",[103,3259,3260,3263,3266,3268],{"class":105,"line":548},[103,3261,3262],{"class":116},"    var",[103,3264,3265],{"class":126}," dbErr ",[103,3267,1306],{"class":116},[103,3269,3270],{"class":133},"DatabaseError\n",[103,3272,3273,3275,3277,3280,3283,3286],{"class":105,"line":570},[103,3274,680],{"class":116},[103,3276,247],{"class":126},[103,3278,3279],{"class":133},"As",[103,3281,3282],{"class":126},"(wrapped, ",[103,3284,3285],{"class":116},"&",[103,3287,3288],{"class":126},"dbErr) {\n",[103,3290,3291,3294,3296,3299],{"class":105,"line":580},[103,3292,3293],{"class":126},"        fmt.",[103,3295,323],{"class":133},[103,3297,3298],{"class":126},"(dbErr.Query) ",[103,3300,3301],{"class":109},"\u002F\u002F \"SELECT\"\n",[103,3303,3304],{"class":105,"line":590},[103,3305,716],{"class":126},[103,3307,3308],{"class":105,"line":1375},[103,3309,146],{"class":126},[148,3311,3312,3314],{},[17,3313,2887],{}," рекурсивно разворачивает цепочку, ища ошибку, assignable к типу целевого указателя, и присваивает её при нахождении.",[90,3316,3318],{"id":3317},"почему-всегда-isas","Почему всегда Is\u002FAs",[95,3320,3322],{"className":97,"code":3321,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nvar ErrNoRows = errors.New(\"sql: no rows in result set\")\n\nfunc queryUser(id int) error {\n    if id == 0 {\n        \u002F\u002F Разработчик добавил контекст — обернул sentinel:\n        return fmt.Errorf(\"users table: %w\", ErrNoRows)\n    }\n    return nil\n}\n\nfunc main() {\n    err := queryUser(0)\n\n    \u002F\u002F ❌ сломалось — err != ErrNoRows напрямую (обёртка)\n    fmt.Println(err == ErrNoRows) \u002F\u002F false\n\n    \u002F\u002F ✅ errors.Is продолжит работать — рекурсивно раскрутит\n    fmt.Println(errors.Is(err, ErrNoRows)) \u002F\u002F true\n}\n",[17,3323,3324,3330,3334,3340,3348,3356,3360,3364,3383,3387,3406,3418,3423,3443,3447,3453,3457,3461,3469,3483,3487,3492,3508,3512,3517,3532],{"__ignoreMap":5},[103,3325,3326,3328],{"class":105,"line":106},[103,3327,167],{"class":116},[103,3329,170],{"class":133},[103,3331,3332],{"class":105,"line":113},[103,3333,176],{"emptyLinePlaceholder":175},[103,3335,3336,3338],{"class":105,"line":130},[103,3337,181],{"class":116},[103,3339,184],{"class":126},[103,3341,3342,3344,3346],{"class":105,"line":143},[103,3343,190],{"class":189},[103,3345,193],{"class":133},[103,3347,196],{"class":189},[103,3349,3350,3352,3354],{"class":105,"line":199},[103,3351,190],{"class":189},[103,3353,204],{"class":133},[103,3355,196],{"class":189},[103,3357,3358],{"class":105,"line":209},[103,3359,212],{"class":126},[103,3361,3362],{"class":105,"line":215},[103,3363,176],{"emptyLinePlaceholder":175},[103,3365,3366,3368,3370,3372,3374,3376,3378,3381],{"class":105,"line":220},[103,3367,403],{"class":116},[103,3369,1165],{"class":126},[103,3371,409],{"class":116},[103,3373,247],{"class":126},[103,3375,250],{"class":133},[103,3377,253],{"class":126},[103,3379,3380],{"class":189},"\"sql: no rows in result set\"",[103,3382,212],{"class":126},[103,3384,3385],{"class":105,"line":232},[103,3386,176],{"emptyLinePlaceholder":175},[103,3388,3389,3391,3394,3396,3398,3400,3402,3404],{"class":105,"line":238},[103,3390,223],{"class":116},[103,3392,3393],{"class":133}," queryUser",[103,3395,253],{"class":126},[103,3397,1240],{"class":658},[103,3399,1243],{"class":116},[103,3401,445],{"class":126},[103,3403,19],{"class":116},[103,3405,127],{"class":126},[103,3407,3408,3410,3412,3414,3416],{"class":105,"line":261},[103,3409,680],{"class":116},[103,3411,1256],{"class":126},[103,3413,686],{"class":116},[103,3415,1261],{"class":280},[103,3417,127],{"class":126},[103,3419,3420],{"class":105,"line":266},[103,3421,3422],{"class":109},"        \u002F\u002F Разработчик добавил контекст — обернул sentinel:\n",[103,3424,3425,3427,3429,3431,3433,3436,3438,3440],{"class":105,"line":272},[103,3426,696],{"class":116},[103,3428,292],{"class":126},[103,3430,295],{"class":133},[103,3432,253],{"class":126},[103,3434,3435],{"class":189},"\"users table: ",[103,3437,1918],{"class":280},[103,3439,1921],{"class":189},[103,3441,3442],{"class":126},", ErrNoRows)\n",[103,3444,3445],{"class":105,"line":284},[103,3446,716],{"class":126},[103,3448,3449,3451],{"class":105,"line":312},[103,3450,721],{"class":116},[103,3452,985],{"class":280},[103,3454,3455],{"class":105,"line":317},[103,3456,146],{"class":126},[103,3458,3459],{"class":105,"line":329},[103,3460,176],{"emptyLinePlaceholder":175},[103,3462,3463,3465,3467],{"class":105,"line":339},[103,3464,223],{"class":116},[103,3466,226],{"class":133},[103,3468,229],{"class":126},[103,3470,3471,3473,3475,3477,3479,3481],{"class":105,"line":506},[103,3472,946],{"class":126},[103,3474,244],{"class":116},[103,3476,3393],{"class":133},[103,3478,253],{"class":126},[103,3480,1432],{"class":280},[103,3482,212],{"class":126},[103,3484,3485],{"class":105,"line":514},[103,3486,176],{"emptyLinePlaceholder":175},[103,3488,3489],{"class":105,"line":523},[103,3490,3491],{"class":109},"    \u002F\u002F ❌ сломалось — err != ErrNoRows напрямую (обёртка)\n",[103,3493,3494,3496,3498,3501,3503,3506],{"class":105,"line":528},[103,3495,320],{"class":126},[103,3497,323],{"class":133},[103,3499,3500],{"class":126},"(err ",[103,3502,686],{"class":116},[103,3504,3505],{"class":126}," ErrNoRows) ",[103,3507,2998],{"class":109},[103,3509,3510],{"class":105,"line":533},[103,3511,176],{"emptyLinePlaceholder":175},[103,3513,3514],{"class":105,"line":542},[103,3515,3516],{"class":109},"    \u002F\u002F ✅ errors.Is продолжит работать — рекурсивно раскрутит\n",[103,3518,3519,3521,3523,3525,3527,3530],{"class":105,"line":548},[103,3520,320],{"class":126},[103,3522,323],{"class":133},[103,3524,555],{"class":126},[103,3526,1335],{"class":133},[103,3528,3529],{"class":126},"(err, ErrNoRows)) ",[103,3531,1940],{"class":109},[103,3533,3534],{"class":105,"line":570},[103,3535,146],{"class":126},[148,3537,3538],{},"Код меняется — обёртки добавляются. Is\u002FAs защищают от этого.",[90,3540,3542],{"id":3541},"запрет-сравнения-через","Запрет сравнения через ==",[95,3544,3546],{"className":97,"code":3545,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\n\u002F\u002F Специально оборачивают без контекста:\nvar errBase = errors.New(\"access denied\")\nvar ErrAccessDenied = fmt.Errorf(\"%w\", errBase)\n\nfunc main() {\n    \u002F\u002F err — обёртка над ErrAccessDenied\n    err := fmt.Errorf(\"handler: %w\", ErrAccessDenied)\n\n    \u002F\u002F Теперь == не сработает НИКОГДА — только errors.Is\n    fmt.Println(err == ErrAccessDenied)          \u002F\u002F false\n    fmt.Println(errors.Is(err, ErrAccessDenied)) \u002F\u002F true\n}\n",[17,3547,3548,3554,3558,3564,3572,3580,3584,3588,3593,3613,3637,3641,3649,3654,3675,3679,3684,3699,3714],{"__ignoreMap":5},[103,3549,3550,3552],{"class":105,"line":106},[103,3551,167],{"class":116},[103,3553,170],{"class":133},[103,3555,3556],{"class":105,"line":113},[103,3557,176],{"emptyLinePlaceholder":175},[103,3559,3560,3562],{"class":105,"line":130},[103,3561,181],{"class":116},[103,3563,184],{"class":126},[103,3565,3566,3568,3570],{"class":105,"line":143},[103,3567,190],{"class":189},[103,3569,193],{"class":133},[103,3571,196],{"class":189},[103,3573,3574,3576,3578],{"class":105,"line":199},[103,3575,190],{"class":189},[103,3577,204],{"class":133},[103,3579,196],{"class":189},[103,3581,3582],{"class":105,"line":209},[103,3583,212],{"class":126},[103,3585,3586],{"class":105,"line":215},[103,3587,176],{"emptyLinePlaceholder":175},[103,3589,3590],{"class":105,"line":220},[103,3591,3592],{"class":109},"\u002F\u002F Специально оборачивают без контекста:\n",[103,3594,3595,3597,3600,3602,3604,3606,3608,3611],{"class":105,"line":232},[103,3596,403],{"class":116},[103,3598,3599],{"class":126}," errBase ",[103,3601,409],{"class":116},[103,3603,247],{"class":126},[103,3605,250],{"class":133},[103,3607,253],{"class":126},[103,3609,3610],{"class":189},"\"access denied\"",[103,3612,212],{"class":126},[103,3614,3615,3617,3620,3622,3624,3626,3628,3630,3632,3634],{"class":105,"line":238},[103,3616,403],{"class":116},[103,3618,3619],{"class":126}," ErrAccessDenied ",[103,3621,409],{"class":116},[103,3623,292],{"class":126},[103,3625,295],{"class":133},[103,3627,253],{"class":126},[103,3629,1921],{"class":189},[103,3631,1918],{"class":280},[103,3633,1921],{"class":189},[103,3635,3636],{"class":126},", errBase)\n",[103,3638,3639],{"class":105,"line":261},[103,3640,176],{"emptyLinePlaceholder":175},[103,3642,3643,3645,3647],{"class":105,"line":266},[103,3644,223],{"class":116},[103,3646,226],{"class":133},[103,3648,229],{"class":126},[103,3650,3651],{"class":105,"line":272},[103,3652,3653],{"class":109},"    \u002F\u002F err — обёртка над ErrAccessDenied\n",[103,3655,3656,3658,3660,3662,3664,3666,3668,3670,3672],{"class":105,"line":284},[103,3657,946],{"class":126},[103,3659,244],{"class":116},[103,3661,292],{"class":126},[103,3663,295],{"class":133},[103,3665,253],{"class":126},[103,3667,3202],{"class":189},[103,3669,1918],{"class":280},[103,3671,1921],{"class":189},[103,3673,3674],{"class":126},", ErrAccessDenied)\n",[103,3676,3677],{"class":105,"line":312},[103,3678,176],{"emptyLinePlaceholder":175},[103,3680,3681],{"class":105,"line":317},[103,3682,3683],{"class":109},"    \u002F\u002F Теперь == не сработает НИКОГДА — только errors.Is\n",[103,3685,3686,3688,3690,3692,3694,3697],{"class":105,"line":329},[103,3687,320],{"class":126},[103,3689,323],{"class":133},[103,3691,3500],{"class":126},[103,3693,686],{"class":116},[103,3695,3696],{"class":126}," ErrAccessDenied)          ",[103,3698,2998],{"class":109},[103,3700,3701,3703,3705,3707,3709,3712],{"class":105,"line":339},[103,3702,320],{"class":126},[103,3704,323],{"class":133},[103,3706,555],{"class":126},[103,3708,1335],{"class":133},[103,3710,3711],{"class":126},"(err, ErrAccessDenied)) ",[103,3713,1940],{"class":109},[103,3715,3716],{"class":105,"line":506},[103,3717,146],{"class":126},[148,3719,3720],{},"Приём для библиотек: заставить пользователей сразу использовать errors.Is.",[85,3722,3724],{"id":3723},"текст-ошибок-и-обработка","Текст ошибок и обработка",[90,3726,3728],{"id":3727},"никогда-не-сравнивай-текст-ошибки","Никогда не сравнивай текст ошибки",[95,3730,3732],{"className":97,"code":3731,"language":99,"meta":5,"style":5},"package main\n\nimport \"errors\"\n\nfunc main() {\n    err := errors.New(\"connection refused\")\n    \u002F\u002F ❌ хрупко, антипаттерн — сравнивать текст ошибки\n    _ = err.Error() == \"connection refused\"\n}\n",[17,3733,3734,3740,3744,3754,3758,3766,3782,3787,3806],{"__ignoreMap":5},[103,3735,3736,3738],{"class":105,"line":106},[103,3737,167],{"class":116},[103,3739,170],{"class":133},[103,3741,3742],{"class":105,"line":113},[103,3743,176],{"emptyLinePlaceholder":175},[103,3745,3746,3748,3750,3752],{"class":105,"line":130},[103,3747,181],{"class":116},[103,3749,1644],{"class":189},[103,3751,193],{"class":133},[103,3753,196],{"class":189},[103,3755,3756],{"class":105,"line":143},[103,3757,176],{"emptyLinePlaceholder":175},[103,3759,3760,3762,3764],{"class":105,"line":199},[103,3761,223],{"class":116},[103,3763,226],{"class":133},[103,3765,229],{"class":126},[103,3767,3768,3770,3772,3774,3776,3778,3780],{"class":105,"line":209},[103,3769,946],{"class":126},[103,3771,244],{"class":116},[103,3773,247],{"class":126},[103,3775,250],{"class":133},[103,3777,253],{"class":126},[103,3779,256],{"class":189},[103,3781,212],{"class":126},[103,3783,3784],{"class":105,"line":215},[103,3785,3786],{"class":109},"    \u002F\u002F ❌ хрупко, антипаттерн — сравнивать текст ошибки\n",[103,3788,3789,3792,3794,3797,3799,3801,3803],{"class":105,"line":220},[103,3790,3791],{"class":126},"    _ ",[103,3793,409],{"class":116},[103,3795,3796],{"class":126}," err.",[103,3798,67],{"class":133},[103,3800,137],{"class":126},[103,3802,686],{"class":116},[103,3804,3805],{"class":189}," \"connection refused\"\n",[103,3807,3808],{"class":105,"line":232},[103,3809,146],{"class":126},[148,3811,3812],{},"Текст ошибки — для людей (логи, экран), не для кода.",[82,3814],{},[11,3816,3817,3824,3831,3838,3845],{},[14,3818,3819,3820,3823],{},"ошибка должна ",[39,3821,3822],{},"рассказывать историю"," — 4 правила текста",[14,3825,3826,3827,3830],{},"обрабатывать ошибку ровно ",[39,3828,3829],{},"один раз"," (логирование = обработка)",[14,3832,3833,3834,3837],{},"не хватает контекста\u002Fответственности → ",[39,3835,3836],{},"пробрасывай"," наверх (с контекстом или без)",[14,3839,3840,3841,3844],{},"есть контекст → ",[39,3842,3843],{},"обрабатывай"," прямо здесь (лог, retry, переподключение и т.д.)",[14,3846,3847],{},"не добавляй бессмысленный контекст — если нечего сказать, просто проксируй",[82,3849],{},[90,3851,3853],{"id":3852},"_4-правила-текста-ошибки","4 правила текста ошибки",[3855,3856,3857,3867,3873,3879],"ol",{},[14,3858,3859,3862,3863,3866],{},[39,3860,3861],{},"Максимально подробно"," — не просто ",[17,3864,3865],{},"\"error\"",", а что именно случилось",[14,3868,3869,3872],{},[39,3870,3871],{},"Весь контекст для расследования"," — получая текст, можно понять причину",[14,3874,3875,3878],{},[39,3876,3877],{},"Однозначно характеризует место"," — одна и та же ошибка не из двух мест кода",[14,3880,3881,3884],{},[39,3882,3883],{},"Не слишком большой"," — логи стоят дорого, не злоупотреблять длиной",[90,3886,3888],{"id":3887},"обрабатывать-один-раз","Обрабатывать один раз",[95,3890,3892],{"className":97,"code":3891,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"log\"\n)\n\nfunc validateCoords(lat, lon float64) error {\n    if lat \u003C -90 || lat > 90 || lon \u003C -180 || lon > 180 {\n        return errors.New(\"coords out of range\")\n    }\n    return nil\n}\n\n\u002F\u002F ❌ двойная обработка — залогировали И пробросили\nfunc GetRouteBad(lat, lon float64) error {\n    err := validateCoords(lat, lon)\n    if err != nil {\n        log.Printf(\"validation failed: %v\", err)     \u002F\u002F обработка 1\n        return fmt.Errorf(\"invalid coords: %w\", err) \u002F\u002F обработка 2\n    }\n    return nil\n}\n\n\u002F\u002F ✅ либо логируем здесь (если хватает контекста)\nfunc GetRouteLog(lat, lon float64) error {\n    err := validateCoords(lat, lon)\n    if err != nil {\n        log.Printf(\"GetRoute: validation failed: %v\", err)\n        return err \u002F\u002F не добавляем контекст, уже залогировали\n    }\n    return nil\n}\n\n\u002F\u002F ✅ либо пробрасываем с контекстом (если не хватает)\nfunc GetRoute(lat, lon float64) error {\n    err := validateCoords(lat, lon)\n    if err != nil {\n        return fmt.Errorf(\"validate coords in GetRoute: %w\", err)\n    }\n    return nil\n}\n\nfunc main() {\n    fmt.Println(GetRoute(200, 0)) \u002F\u002F coords out of range\n}\n",[17,3893,3894,3900,3904,3910,3918,3926,3935,3939,3943,3969,4019,4034,4038,4044,4048,4052,4057,4080,4091,4103,4126,4149,4153,4159,4163,4167,4172,4195,4205,4217,4235,4244,4248,4254,4258,4262,4267,4290,4300,4312,4332,4337,4344,4349,4354,4363,4389],{"__ignoreMap":5},[103,3895,3896,3898],{"class":105,"line":106},[103,3897,167],{"class":116},[103,3899,170],{"class":133},[103,3901,3902],{"class":105,"line":113},[103,3903,176],{"emptyLinePlaceholder":175},[103,3905,3906,3908],{"class":105,"line":130},[103,3907,181],{"class":116},[103,3909,184],{"class":126},[103,3911,3912,3914,3916],{"class":105,"line":143},[103,3913,190],{"class":189},[103,3915,193],{"class":133},[103,3917,196],{"class":189},[103,3919,3920,3922,3924],{"class":105,"line":199},[103,3921,190],{"class":189},[103,3923,204],{"class":133},[103,3925,196],{"class":189},[103,3927,3928,3930,3933],{"class":105,"line":209},[103,3929,190],{"class":189},[103,3931,3932],{"class":133},"log",[103,3934,196],{"class":189},[103,3936,3937],{"class":105,"line":215},[103,3938,212],{"class":126},[103,3940,3941],{"class":105,"line":220},[103,3942,176],{"emptyLinePlaceholder":175},[103,3944,3945,3947,3950,3952,3955,3957,3960,3963,3965,3967],{"class":105,"line":232},[103,3946,223],{"class":116},[103,3948,3949],{"class":133}," validateCoords",[103,3951,253],{"class":126},[103,3953,3954],{"class":658},"lat",[103,3956,57],{"class":126},[103,3958,3959],{"class":658},"lon",[103,3961,3962],{"class":116}," float64",[103,3964,445],{"class":126},[103,3966,19],{"class":116},[103,3968,127],{"class":126},[103,3970,3971,3973,3976,3979,3982,3985,3988,3990,3993,3996,3998,4001,4003,4005,4008,4010,4012,4014,4017],{"class":105,"line":238},[103,3972,680],{"class":116},[103,3974,3975],{"class":126}," lat ",[103,3977,3978],{"class":116},"\u003C",[103,3980,3981],{"class":116}," -",[103,3983,3984],{"class":280},"90",[103,3986,3987],{"class":116}," ||",[103,3989,3975],{"class":126},[103,3991,3992],{"class":116},">",[103,3994,3995],{"class":280}," 90",[103,3997,3987],{"class":116},[103,3999,4000],{"class":126}," lon ",[103,4002,3978],{"class":116},[103,4004,3981],{"class":116},[103,4006,4007],{"class":280},"180",[103,4009,3987],{"class":116},[103,4011,4000],{"class":126},[103,4013,3992],{"class":116},[103,4015,4016],{"class":280}," 180",[103,4018,127],{"class":126},[103,4020,4021,4023,4025,4027,4029,4032],{"class":105,"line":261},[103,4022,696],{"class":116},[103,4024,247],{"class":126},[103,4026,250],{"class":133},[103,4028,253],{"class":126},[103,4030,4031],{"class":189},"\"coords out of range\"",[103,4033,212],{"class":126},[103,4035,4036],{"class":105,"line":266},[103,4037,716],{"class":126},[103,4039,4040,4042],{"class":105,"line":272},[103,4041,721],{"class":116},[103,4043,985],{"class":280},[103,4045,4046],{"class":105,"line":284},[103,4047,146],{"class":126},[103,4049,4050],{"class":105,"line":312},[103,4051,176],{"emptyLinePlaceholder":175},[103,4053,4054],{"class":105,"line":317},[103,4055,4056],{"class":109},"\u002F\u002F ❌ двойная обработка — залогировали И пробросили\n",[103,4058,4059,4061,4064,4066,4068,4070,4072,4074,4076,4078],{"class":105,"line":329},[103,4060,223],{"class":116},[103,4062,4063],{"class":133}," GetRouteBad",[103,4065,253],{"class":126},[103,4067,3954],{"class":658},[103,4069,57],{"class":126},[103,4071,3959],{"class":658},[103,4073,3962],{"class":116},[103,4075,445],{"class":126},[103,4077,19],{"class":116},[103,4079,127],{"class":126},[103,4081,4082,4084,4086,4088],{"class":105,"line":339},[103,4083,946],{"class":126},[103,4085,244],{"class":116},[103,4087,3949],{"class":133},[103,4089,4090],{"class":126},"(lat, lon)\n",[103,4092,4093,4095,4097,4099,4101],{"class":105,"line":506},[103,4094,680],{"class":116},[103,4096,960],{"class":126},[103,4098,963],{"class":116},[103,4100,699],{"class":280},[103,4102,127],{"class":126},[103,4104,4105,4108,4111,4113,4116,4118,4120,4123],{"class":105,"line":514},[103,4106,4107],{"class":126},"        log.",[103,4109,4110],{"class":133},"Printf",[103,4112,253],{"class":126},[103,4114,4115],{"class":189},"\"validation failed: ",[103,4117,1977],{"class":280},[103,4119,1921],{"class":189},[103,4121,4122],{"class":126},", err)     ",[103,4124,4125],{"class":109},"\u002F\u002F обработка 1\n",[103,4127,4128,4130,4132,4134,4136,4139,4141,4143,4146],{"class":105,"line":523},[103,4129,696],{"class":116},[103,4131,292],{"class":126},[103,4133,295],{"class":133},[103,4135,253],{"class":126},[103,4137,4138],{"class":189},"\"invalid coords: ",[103,4140,1918],{"class":280},[103,4142,1921],{"class":189},[103,4144,4145],{"class":126},", err) ",[103,4147,4148],{"class":109},"\u002F\u002F обработка 2\n",[103,4150,4151],{"class":105,"line":528},[103,4152,716],{"class":126},[103,4154,4155,4157],{"class":105,"line":533},[103,4156,721],{"class":116},[103,4158,985],{"class":280},[103,4160,4161],{"class":105,"line":542},[103,4162,146],{"class":126},[103,4164,4165],{"class":105,"line":548},[103,4166,176],{"emptyLinePlaceholder":175},[103,4168,4169],{"class":105,"line":570},[103,4170,4171],{"class":109},"\u002F\u002F ✅ либо логируем здесь (если хватает контекста)\n",[103,4173,4174,4176,4179,4181,4183,4185,4187,4189,4191,4193],{"class":105,"line":580},[103,4175,223],{"class":116},[103,4177,4178],{"class":133}," GetRouteLog",[103,4180,253],{"class":126},[103,4182,3954],{"class":658},[103,4184,57],{"class":126},[103,4186,3959],{"class":658},[103,4188,3962],{"class":116},[103,4190,445],{"class":126},[103,4192,19],{"class":116},[103,4194,127],{"class":126},[103,4196,4197,4199,4201,4203],{"class":105,"line":590},[103,4198,946],{"class":126},[103,4200,244],{"class":116},[103,4202,3949],{"class":133},[103,4204,4090],{"class":126},[103,4206,4207,4209,4211,4213,4215],{"class":105,"line":1375},[103,4208,680],{"class":116},[103,4210,960],{"class":126},[103,4212,963],{"class":116},[103,4214,699],{"class":280},[103,4216,127],{"class":126},[103,4218,4219,4221,4223,4225,4228,4230,4232],{"class":105,"line":1380},[103,4220,4107],{"class":126},[103,4222,4110],{"class":133},[103,4224,253],{"class":126},[103,4226,4227],{"class":189},"\"GetRoute: validation failed: ",[103,4229,1977],{"class":280},[103,4231,1921],{"class":189},[103,4233,4234],{"class":126},", err)\n",[103,4236,4237,4239,4241],{"class":105,"line":1401},[103,4238,696],{"class":116},[103,4240,960],{"class":126},[103,4242,4243],{"class":109},"\u002F\u002F не добавляем контекст, уже залогировали\n",[103,4245,4246],{"class":105,"line":1406},[103,4247,716],{"class":126},[103,4249,4250,4252],{"class":105,"line":1411},[103,4251,721],{"class":116},[103,4253,985],{"class":280},[103,4255,4256],{"class":105,"line":1420},[103,4257,146],{"class":126},[103,4259,4260],{"class":105,"line":1437},[103,4261,176],{"emptyLinePlaceholder":175},[103,4263,4264],{"class":105,"line":1450},[103,4265,4266],{"class":109},"\u002F\u002F ✅ либо пробрасываем с контекстом (если не хватает)\n",[103,4268,4269,4271,4274,4276,4278,4280,4282,4284,4286,4288],{"class":105,"line":1455},[103,4270,223],{"class":116},[103,4272,4273],{"class":133}," GetRoute",[103,4275,253],{"class":126},[103,4277,3954],{"class":658},[103,4279,57],{"class":126},[103,4281,3959],{"class":658},[103,4283,3962],{"class":116},[103,4285,445],{"class":126},[103,4287,19],{"class":116},[103,4289,127],{"class":126},[103,4291,4292,4294,4296,4298],{"class":105,"line":1471},[103,4293,946],{"class":126},[103,4295,244],{"class":116},[103,4297,3949],{"class":133},[103,4299,4090],{"class":126},[103,4301,4302,4304,4306,4308,4310],{"class":105,"line":1483},[103,4303,680],{"class":116},[103,4305,960],{"class":126},[103,4307,963],{"class":116},[103,4309,699],{"class":280},[103,4311,127],{"class":126},[103,4313,4315,4317,4319,4321,4323,4326,4328,4330],{"class":105,"line":4314},40,[103,4316,696],{"class":116},[103,4318,292],{"class":126},[103,4320,295],{"class":133},[103,4322,253],{"class":126},[103,4324,4325],{"class":189},"\"validate coords in GetRoute: ",[103,4327,1918],{"class":280},[103,4329,1921],{"class":189},[103,4331,4234],{"class":126},[103,4333,4335],{"class":105,"line":4334},41,[103,4336,716],{"class":126},[103,4338,4340,4342],{"class":105,"line":4339},42,[103,4341,721],{"class":116},[103,4343,985],{"class":280},[103,4345,4347],{"class":105,"line":4346},43,[103,4348,146],{"class":126},[103,4350,4352],{"class":105,"line":4351},44,[103,4353,176],{"emptyLinePlaceholder":175},[103,4355,4357,4359,4361],{"class":105,"line":4356},45,[103,4358,223],{"class":116},[103,4360,226],{"class":133},[103,4362,229],{"class":126},[103,4364,4366,4368,4370,4372,4375,4377,4380,4382,4384,4386],{"class":105,"line":4365},46,[103,4367,320],{"class":126},[103,4369,323],{"class":133},[103,4371,253],{"class":126},[103,4373,4374],{"class":133},"GetRoute",[103,4376,253],{"class":126},[103,4378,4379],{"class":280},"200",[103,4381,57],{"class":126},[103,4383,1432],{"class":280},[103,4385,564],{"class":126},[103,4387,4388],{"class":109},"\u002F\u002F coords out of range\n",[103,4390,4392],{"class":105,"line":4391},47,[103,4393,146],{"class":126},[90,4395,4397],{"id":4396},"проброс-не-обработка","Проброс — не обработка",[148,4399,4400,4401,4403,4404,4407],{},"Проброс ошибки наверх (с контекстом или без) — это ",[39,4402,1097],{}," обработка. Обработка — это когда вы что-то ",[39,4405,4406],{},"делаете",": логируете, делаете retry, отвечаете клиенту, меняете поведение.",[90,4409,4411],{"id":4410},"не-добавляй-пустой-контекст","Не добавляй пустой контекст",[95,4413,4415],{"className":97,"code":4414,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc wrap(err error, path string) error {\n    \u002F\u002F ❌ контекст бессмысленный\n    \u002F\u002F return fmt.Errorf(\"error: %w\", err)\n\n    \u002F\u002F ✅ просто проксируй\n    \u002F\u002F return err\n\n    \u002F\u002F ✅ или добавь полезный контекст\n    return fmt.Errorf(\"reading config file %s: %w\", path, err)\n}\n\nfunc main() {\n    err := errors.New(\"permission denied\")\n    fmt.Println(wrap(err, \"\u002Fetc\u002Fapp.conf\"))\n}\n",[17,4416,4417,4423,4427,4433,4441,4449,4453,4457,4482,4487,4492,4496,4501,4506,4510,4515,4538,4542,4546,4554,4571,4590],{"__ignoreMap":5},[103,4418,4419,4421],{"class":105,"line":106},[103,4420,167],{"class":116},[103,4422,170],{"class":133},[103,4424,4425],{"class":105,"line":113},[103,4426,176],{"emptyLinePlaceholder":175},[103,4428,4429,4431],{"class":105,"line":130},[103,4430,181],{"class":116},[103,4432,184],{"class":126},[103,4434,4435,4437,4439],{"class":105,"line":143},[103,4436,190],{"class":189},[103,4438,193],{"class":133},[103,4440,196],{"class":189},[103,4442,4443,4445,4447],{"class":105,"line":199},[103,4444,190],{"class":189},[103,4446,204],{"class":133},[103,4448,196],{"class":189},[103,4450,4451],{"class":105,"line":209},[103,4452,212],{"class":126},[103,4454,4455],{"class":105,"line":215},[103,4456,176],{"emptyLinePlaceholder":175},[103,4458,4459,4461,4464,4466,4468,4470,4472,4474,4476,4478,4480],{"class":105,"line":220},[103,4460,223],{"class":116},[103,4462,4463],{"class":133}," wrap",[103,4465,253],{"class":126},[103,4467,52],{"class":658},[103,4469,120],{"class":116},[103,4471,57],{"class":126},[103,4473,659],{"class":658},[103,4475,662],{"class":116},[103,4477,445],{"class":126},[103,4479,19],{"class":116},[103,4481,127],{"class":126},[103,4483,4484],{"class":105,"line":232},[103,4485,4486],{"class":109},"    \u002F\u002F ❌ контекст бессмысленный\n",[103,4488,4489],{"class":105,"line":238},[103,4490,4491],{"class":109},"    \u002F\u002F return fmt.Errorf(\"error: %w\", err)\n",[103,4493,4494],{"class":105,"line":261},[103,4495,176],{"emptyLinePlaceholder":175},[103,4497,4498],{"class":105,"line":266},[103,4499,4500],{"class":109},"    \u002F\u002F ✅ просто проксируй\n",[103,4502,4503],{"class":105,"line":272},[103,4504,4505],{"class":109},"    \u002F\u002F return err\n",[103,4507,4508],{"class":105,"line":284},[103,4509,176],{"emptyLinePlaceholder":175},[103,4511,4512],{"class":105,"line":312},[103,4513,4514],{"class":109},"    \u002F\u002F ✅ или добавь полезный контекст\n",[103,4516,4517,4519,4521,4523,4525,4528,4530,4532,4534,4536],{"class":105,"line":317},[103,4518,721],{"class":116},[103,4520,292],{"class":126},[103,4522,295],{"class":133},[103,4524,253],{"class":126},[103,4526,4527],{"class":189},"\"reading config file ",[103,4529,2494],{"class":280},[103,4531,2497],{"class":189},[103,4533,1918],{"class":280},[103,4535,1921],{"class":189},[103,4537,2504],{"class":126},[103,4539,4540],{"class":105,"line":329},[103,4541,146],{"class":126},[103,4543,4544],{"class":105,"line":339},[103,4545,176],{"emptyLinePlaceholder":175},[103,4547,4548,4550,4552],{"class":105,"line":506},[103,4549,223],{"class":116},[103,4551,226],{"class":133},[103,4553,229],{"class":126},[103,4555,4556,4558,4560,4562,4564,4566,4569],{"class":105,"line":514},[103,4557,946],{"class":126},[103,4559,244],{"class":116},[103,4561,247],{"class":126},[103,4563,250],{"class":133},[103,4565,253],{"class":126},[103,4567,4568],{"class":189},"\"permission denied\"",[103,4570,212],{"class":126},[103,4572,4573,4575,4577,4579,4582,4585,4588],{"class":105,"line":523},[103,4574,320],{"class":126},[103,4576,323],{"class":133},[103,4578,253],{"class":126},[103,4580,4581],{"class":133},"wrap",[103,4583,4584],{"class":126},"(err, ",[103,4586,4587],{"class":189},"\"\u002Fetc\u002Fapp.conf\"",[103,4589,2649],{"class":126},[103,4591,4592],{"class":105,"line":528},[103,4593,146],{"class":126},[82,4595],{},[11,4597,4598,4605,4615,4618,4628],{},[14,4599,4600,4601,4604],{},"Go ",[39,4602,4603],{},"не поддерживает"," исключения (exceptions) — вместо них явная обработка ошибок",[14,4606,4607,4610,4611,4614],{},[17,4608,4609],{},"panic"," — механизм для ",[39,4612,4613],{},"исключительных"," ситуаций, когда продолжение невозможно",[14,4616,4617],{},"panic ≠ throw: throw делает проблему вызывающей стороны, panic = «не знаю что делать, сдаюсь»",[14,4619,4620,4623,4624,4627],{},[17,4621,4622],{},"recover"," работает ",[39,4625,4626],{},"только в defer"," — вне defer возвращает nil",[14,4629,4630,4631,4634],{},"panic принимает ",[17,4632,4633],{},"any"," — можно передать строку, ошибку, что угодно, даже nil",[82,4636],{},[85,4638,4640],{"id":4639},"panic-recover","panic \u002F recover",[90,4642,4644],{"id":4643},"panic-recover-1","Panic + recover",[95,4646,4648],{"className":97,"code":4647,"language":99,"meta":5,"style":5},"package main\n\nimport \"fmt\"\n\nfunc main() {\n    defer func() {\n        if r := recover(); r != nil {\n            fmt.Println(\"recovered:\", r)\n        }\n    }()\n\n    fmt.Println(\"start\")\n    panic(\"something terrible\")\n    fmt.Println(\"unreachable\") \u002F\u002F никогда не выполнится\n}\n\n\u002F\u002F start\n\u002F\u002F recovered: something terrible\n",[17,4649,4650,4656,4660,4670,4674,4682,4692,4714,4729,4734,4739,4743,4756,4768,4784,4788,4792,4797],{"__ignoreMap":5},[103,4651,4652,4654],{"class":105,"line":106},[103,4653,167],{"class":116},[103,4655,170],{"class":133},[103,4657,4658],{"class":105,"line":113},[103,4659,176],{"emptyLinePlaceholder":175},[103,4661,4662,4664,4666,4668],{"class":105,"line":130},[103,4663,181],{"class":116},[103,4665,1644],{"class":189},[103,4667,204],{"class":133},[103,4669,196],{"class":189},[103,4671,4672],{"class":105,"line":143},[103,4673,176],{"emptyLinePlaceholder":175},[103,4675,4676,4678,4680],{"class":105,"line":199},[103,4677,223],{"class":116},[103,4679,226],{"class":133},[103,4681,229],{"class":126},[103,4683,4684,4687,4690],{"class":105,"line":209},[103,4685,4686],{"class":116},"    defer",[103,4688,4689],{"class":116}," func",[103,4691,229],{"class":126},[103,4693,4694,4697,4700,4702,4705,4708,4710,4712],{"class":105,"line":215},[103,4695,4696],{"class":116},"        if",[103,4698,4699],{"class":126}," r ",[103,4701,244],{"class":116},[103,4703,4704],{"class":133}," recover",[103,4706,4707],{"class":126},"(); r ",[103,4709,963],{"class":116},[103,4711,699],{"class":280},[103,4713,127],{"class":126},[103,4715,4716,4719,4721,4723,4726],{"class":105,"line":220},[103,4717,4718],{"class":126},"            fmt.",[103,4720,323],{"class":133},[103,4722,253],{"class":126},[103,4724,4725],{"class":189},"\"recovered:\"",[103,4727,4728],{"class":126},", r)\n",[103,4730,4731],{"class":105,"line":232},[103,4732,4733],{"class":126},"        }\n",[103,4735,4736],{"class":105,"line":238},[103,4737,4738],{"class":126},"    }()\n",[103,4740,4741],{"class":105,"line":261},[103,4742,176],{"emptyLinePlaceholder":175},[103,4744,4745,4747,4749,4751,4754],{"class":105,"line":266},[103,4746,320],{"class":126},[103,4748,323],{"class":133},[103,4750,253],{"class":126},[103,4752,4753],{"class":189},"\"start\"",[103,4755,212],{"class":126},[103,4757,4758,4761,4763,4766],{"class":105,"line":272},[103,4759,4760],{"class":133},"    panic",[103,4762,253],{"class":126},[103,4764,4765],{"class":189},"\"something terrible\"",[103,4767,212],{"class":126},[103,4769,4770,4772,4774,4776,4779,4781],{"class":105,"line":284},[103,4771,320],{"class":126},[103,4773,323],{"class":133},[103,4775,253],{"class":126},[103,4777,4778],{"class":189},"\"unreachable\"",[103,4780,445],{"class":126},[103,4782,4783],{"class":109},"\u002F\u002F никогда не выполнится\n",[103,4785,4786],{"class":105,"line":312},[103,4787,146],{"class":126},[103,4789,4790],{"class":105,"line":317},[103,4791,176],{"emptyLinePlaceholder":175},[103,4793,4794],{"class":105,"line":329},[103,4795,4796],{"class":109},"\u002F\u002F start\n",[103,4798,4799],{"class":105,"line":339},[103,4800,4801],{"class":109},"\u002F\u002F recovered: something terrible\n",[90,4803,4805],{"id":4804},"stack-unwinding-раскрутка-стека","Stack unwinding (раскрутка стека)",[95,4807,4812],{"className":4808,"code":4810,"language":4811},[4809],"language-text","1. panic() → остановка текущей функции\n2. вызов defer'ов текущей функции (LIFO)\n3. выход → вызов defer'ов вызывающей функции\n4. ... и так вверх по стеку\n5. Если нашли recover в defer → восстановление\n6. Если не нашли → завершение программы с трейсом\n","text",[17,4813,4810],{"__ignoreMap":5},[90,4815,4817],{"id":4816},"recover-только-в-defer","Recover только в defer",[95,4819,4821],{"className":97,"code":4820,"language":99,"meta":5,"style":5},"package main\n\nimport \"fmt\"\n\nfunc process() {\n    recover()                     \u002F\u002F ❌ бесполезно — вернёт nil\n    defer recover()               \u002F\u002F ❌ тоже не сработает (не вызов в defer-функции)\n    defer func() { recover() }() \u002F\u002F ✅ работает\n    panic(\"error\")\n}\n\nfunc main() {\n    process()\n    fmt.Println(\"survived\") \u002F\u002F выполнится, потому что panic перехвачен\n}\n",[17,4822,4823,4829,4833,4843,4847,4856,4867,4879,4896,4906,4910,4914,4922,4929,4945],{"__ignoreMap":5},[103,4824,4825,4827],{"class":105,"line":106},[103,4826,167],{"class":116},[103,4828,170],{"class":133},[103,4830,4831],{"class":105,"line":113},[103,4832,176],{"emptyLinePlaceholder":175},[103,4834,4835,4837,4839,4841],{"class":105,"line":130},[103,4836,181],{"class":116},[103,4838,1644],{"class":189},[103,4840,204],{"class":133},[103,4842,196],{"class":189},[103,4844,4845],{"class":105,"line":143},[103,4846,176],{"emptyLinePlaceholder":175},[103,4848,4849,4851,4854],{"class":105,"line":199},[103,4850,223],{"class":116},[103,4852,4853],{"class":133}," process",[103,4855,229],{"class":126},[103,4857,4858,4861,4864],{"class":105,"line":209},[103,4859,4860],{"class":133},"    recover",[103,4862,4863],{"class":126},"()                     ",[103,4865,4866],{"class":109},"\u002F\u002F ❌ бесполезно — вернёт nil\n",[103,4868,4869,4871,4873,4876],{"class":105,"line":215},[103,4870,4686],{"class":116},[103,4872,4704],{"class":133},[103,4874,4875],{"class":126},"()               ",[103,4877,4878],{"class":109},"\u002F\u002F ❌ тоже не сработает (не вызов в defer-функции)\n",[103,4880,4881,4883,4885,4888,4890,4893],{"class":105,"line":220},[103,4882,4686],{"class":116},[103,4884,4689],{"class":116},[103,4886,4887],{"class":126},"() { ",[103,4889,4622],{"class":133},[103,4891,4892],{"class":126},"() }() ",[103,4894,4895],{"class":109},"\u002F\u002F ✅ работает\n",[103,4897,4898,4900,4902,4904],{"class":105,"line":232},[103,4899,4760],{"class":133},[103,4901,253],{"class":126},[103,4903,3865],{"class":189},[103,4905,212],{"class":126},[103,4907,4908],{"class":105,"line":238},[103,4909,146],{"class":126},[103,4911,4912],{"class":105,"line":261},[103,4913,176],{"emptyLinePlaceholder":175},[103,4915,4916,4918,4920],{"class":105,"line":266},[103,4917,223],{"class":116},[103,4919,226],{"class":133},[103,4921,229],{"class":126},[103,4923,4924,4927],{"class":105,"line":272},[103,4925,4926],{"class":133},"    process",[103,4928,953],{"class":126},[103,4930,4931,4933,4935,4937,4940,4942],{"class":105,"line":284},[103,4932,320],{"class":126},[103,4934,323],{"class":133},[103,4936,253],{"class":126},[103,4938,4939],{"class":189},"\"survived\"",[103,4941,445],{"class":126},[103,4943,4944],{"class":109},"\u002F\u002F выполнится, потому что panic перехвачен\n",[103,4946,4947],{"class":105,"line":312},[103,4948,146],{"class":126},[148,4950,4951,4954,4955,4957,4958,4961,4962,4964,4965,4967,4968,2368],{},[17,4952,4953],{},"defer recover()"," не работает, потому что ",[17,4956,4622],{}," должен быть вызван ",[39,4959,4960],{},"прямо внутри deferred-функции",". В форме ",[17,4963,4953],{}," сам вызов действительно откладывается, но выполняется как deferred call к ",[17,4966,4622],{},", а не внутри функции, которую рантайм считает обработчиком паники. Правильно: ",[17,4969,4970],{},"defer func() { recover() }()",[90,4972,4974],{"id":4973},"panic-принимает-any","Panic принимает any",[95,4976,4978],{"className":97,"code":4977,"language":99,"meta":5,"style":5},"package main\n\nimport \"errors\"\n\nfunc main() {\n    \u002F\u002F panic принимает любой тип (any):\n    \u002F\u002F panic(\"string error\")           \u002F\u002F строка\n    \u002F\u002F panic(errors.New(\"real error\"))  \u002F\u002F error\n    \u002F\u002F panic(42)                        \u002F\u002F int\n    \u002F\u002F panic(nil)                       \u002F\u002F nil → Go 1.21+ создаёт runtime.PanicNilError\n\n    \u002F\u002F Запустить можно любой из них:\n    panic(errors.New(\"real error\"))\n}\n",[17,4979,4980,4986,4990,5000,5004,5012,5017,5025,5033,5041,5049,5053,5058,5073],{"__ignoreMap":5},[103,4981,4982,4984],{"class":105,"line":106},[103,4983,167],{"class":116},[103,4985,170],{"class":133},[103,4987,4988],{"class":105,"line":113},[103,4989,176],{"emptyLinePlaceholder":175},[103,4991,4992,4994,4996,4998],{"class":105,"line":130},[103,4993,181],{"class":116},[103,4995,1644],{"class":189},[103,4997,193],{"class":133},[103,4999,196],{"class":189},[103,5001,5002],{"class":105,"line":143},[103,5003,176],{"emptyLinePlaceholder":175},[103,5005,5006,5008,5010],{"class":105,"line":199},[103,5007,223],{"class":116},[103,5009,226],{"class":133},[103,5011,229],{"class":126},[103,5013,5014],{"class":105,"line":209},[103,5015,5016],{"class":109},"    \u002F\u002F panic принимает любой тип (any):\n",[103,5018,5019,5022],{"class":105,"line":215},[103,5020,5021],{"class":109},"    \u002F\u002F panic(\"string error\")",[103,5023,5024],{"class":109},"           \u002F\u002F строка\n",[103,5026,5027,5030],{"class":105,"line":220},[103,5028,5029],{"class":109},"    \u002F\u002F panic(errors.New(\"real error\"))",[103,5031,5032],{"class":109},"  \u002F\u002F error\n",[103,5034,5035,5038],{"class":105,"line":232},[103,5036,5037],{"class":109},"    \u002F\u002F panic(42)",[103,5039,5040],{"class":109},"                        \u002F\u002F int\n",[103,5042,5043,5046],{"class":105,"line":238},[103,5044,5045],{"class":109},"    \u002F\u002F panic(nil)",[103,5047,5048],{"class":109},"                       \u002F\u002F nil → Go 1.21+ создаёт runtime.PanicNilError\n",[103,5050,5051],{"class":105,"line":261},[103,5052,176],{"emptyLinePlaceholder":175},[103,5054,5055],{"class":105,"line":266},[103,5056,5057],{"class":109},"    \u002F\u002F Запустить можно любой из них:\n",[103,5059,5060,5062,5064,5066,5068,5071],{"class":105,"line":272},[103,5061,4760],{"class":133},[103,5063,555],{"class":126},[103,5065,250],{"class":133},[103,5067,253],{"class":126},[103,5069,5070],{"class":189},"\"real error\"",[103,5072,2649],{"class":126},[103,5074,5075],{"class":105,"line":284},[103,5076,146],{"class":126},[90,5078,5080],{"id":5079},"когда-использовать-panic","Когда использовать panic",[148,5082,5083,5084,5087,5088,2368],{},"Ошибка программиста (пришло значение, которое ",[39,5085,5086],{},"никогда"," не должно было прийти). Зависимость не инициализировалась (база не пингуется, без неё приложение бессмысленно). Исключительная ситуация, когда ",[39,5089,5090],{},"нечего обрабатывать",[90,5092,5094],{"id":5093},"почему-go-без-исключений","Почему Go без исключений",[148,5096,5097],{},"Исключения = сложность (гарантии безопасности, утечки ресурсов в C++, неявность потока управления). Go позиционируется как простой язык — явная обработка ошибок проще для понимания.",[82,5099],{},[11,5101,5102,5108,5114,5121,5127],{},[14,5103,5104,5107],{},[39,5105,5106],{},"sentinel"," — когда нужна маркировка без дополнительного контекста (io.EOF, sql.ErrNoRows)",[14,5109,5110,5113],{},[39,5111,5112],{},"кастомный тип"," — когда нужен контекст: поля с параметрами (os.PathError: Op, Path, Err)",[14,5115,5116,5117,5120],{},"оба создают ",[39,5118,5119],{},"зависимость"," между пакетами (нужен импорт для сравнения)",[14,5122,5123,5126],{},[39,5124,5125],{},"проверка по поведению"," — type assertion к анонимному интерфейсу, разрывает зависимость",[14,5128,5129,5130,5133],{},"ошибки = ",[39,5131,5132],{},"часть публичного API"," пакета, относиться бережно",[82,5135],{},[85,5137,5139],{"id":5138},"дизайн-error-api","Дизайн error API",[90,5141,5143],{"id":5142},"sentinel-маркировка","Sentinel — маркировка",[95,5145,5147],{"className":97,"code":5146,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nvar ErrNotFound = errors.New(\"not found\")\nvar ErrTimeout = errors.New(\"timeout\")\n\nfunc find(id int) error {\n    if id == 0 {\n        return ErrNotFound\n    }\n    return nil\n}\n\nfunc main() {\n    err := find(0)\n    \u002F\u002F Проверка:\n    fmt.Println(errors.Is(err, ErrNotFound)) \u002F\u002F true\n}\n",[17,5148,5149,5155,5159,5165,5173,5181,5185,5189,5207,5226,5230,5249,5261,5268,5272,5278,5282,5286,5294,5308,5313,5328],{"__ignoreMap":5},[103,5150,5151,5153],{"class":105,"line":106},[103,5152,167],{"class":116},[103,5154,170],{"class":133},[103,5156,5157],{"class":105,"line":113},[103,5158,176],{"emptyLinePlaceholder":175},[103,5160,5161,5163],{"class":105,"line":130},[103,5162,181],{"class":116},[103,5164,184],{"class":126},[103,5166,5167,5169,5171],{"class":105,"line":143},[103,5168,190],{"class":189},[103,5170,193],{"class":133},[103,5172,196],{"class":189},[103,5174,5175,5177,5179],{"class":105,"line":199},[103,5176,190],{"class":189},[103,5178,204],{"class":133},[103,5180,196],{"class":189},[103,5182,5183],{"class":105,"line":209},[103,5184,212],{"class":126},[103,5186,5187],{"class":105,"line":215},[103,5188,176],{"emptyLinePlaceholder":175},[103,5190,5191,5193,5195,5197,5199,5201,5203,5205],{"class":105,"line":220},[103,5192,403],{"class":116},[103,5194,406],{"class":126},[103,5196,409],{"class":116},[103,5198,247],{"class":126},[103,5200,250],{"class":133},[103,5202,253],{"class":126},[103,5204,418],{"class":189},[103,5206,212],{"class":126},[103,5208,5209,5211,5214,5216,5218,5220,5222,5224],{"class":105,"line":232},[103,5210,403],{"class":116},[103,5212,5213],{"class":126}," ErrTimeout ",[103,5215,409],{"class":116},[103,5217,247],{"class":126},[103,5219,250],{"class":133},[103,5221,253],{"class":126},[103,5223,3184],{"class":189},[103,5225,212],{"class":126},[103,5227,5228],{"class":105,"line":238},[103,5229,176],{"emptyLinePlaceholder":175},[103,5231,5232,5234,5237,5239,5241,5243,5245,5247],{"class":105,"line":261},[103,5233,223],{"class":116},[103,5235,5236],{"class":133}," find",[103,5238,253],{"class":126},[103,5240,1240],{"class":658},[103,5242,1243],{"class":116},[103,5244,445],{"class":126},[103,5246,19],{"class":116},[103,5248,127],{"class":126},[103,5250,5251,5253,5255,5257,5259],{"class":105,"line":266},[103,5252,680],{"class":116},[103,5254,1256],{"class":126},[103,5256,686],{"class":116},[103,5258,1261],{"class":280},[103,5260,127],{"class":126},[103,5262,5263,5265],{"class":105,"line":272},[103,5264,696],{"class":116},[103,5266,5267],{"class":126}," ErrNotFound\n",[103,5269,5270],{"class":105,"line":284},[103,5271,716],{"class":126},[103,5273,5274,5276],{"class":105,"line":312},[103,5275,721],{"class":116},[103,5277,985],{"class":280},[103,5279,5280],{"class":105,"line":317},[103,5281,146],{"class":126},[103,5283,5284],{"class":105,"line":329},[103,5285,176],{"emptyLinePlaceholder":175},[103,5287,5288,5290,5292],{"class":105,"line":339},[103,5289,223],{"class":116},[103,5291,226],{"class":133},[103,5293,229],{"class":126},[103,5295,5296,5298,5300,5302,5304,5306],{"class":105,"line":506},[103,5297,946],{"class":126},[103,5299,244],{"class":116},[103,5301,5236],{"class":133},[103,5303,253],{"class":126},[103,5305,1432],{"class":280},[103,5307,212],{"class":126},[103,5309,5310],{"class":105,"line":514},[103,5311,5312],{"class":109},"    \u002F\u002F Проверка:\n",[103,5314,5315,5317,5319,5321,5323,5326],{"class":105,"line":523},[103,5316,320],{"class":126},[103,5318,323],{"class":133},[103,5320,555],{"class":126},[103,5322,1335],{"class":133},[103,5324,5325],{"class":126},"(err, ErrNotFound)) ",[103,5327,1940],{"class":109},[103,5329,5330],{"class":105,"line":528},[103,5331,146],{"class":126},[148,5333,5334],{},"Просто, без полей. Достаточно, когда не нужен контекст.",[90,5336,5338],{"id":5337},"кастомный-тип-контекст","Кастомный тип — контекст",[95,5340,5342],{"className":97,"code":5341,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\n\u002F\u002F Пример: кастомный тип с контекстом (аналог os.PathError)\ntype PathError struct {\n    Op   string \u002F\u002F \"open\", \"read\"\n    Path string \u002F\u002F \"\u002Fetc\u002Fconfig\"\n    Err  error  \u002F\u002F underlying error\n}\n\nfunc (e *PathError) Error() string {\n    return e.Op + \" \" + e.Path + \": \" + e.Err.Error()\n}\n\nfunc openFile(path string) error {\n    return &PathError{Op: \"open\", Path: path, Err: errors.New(\"no such file\")}\n}\n\nfunc main() {\n    err := openFile(\"\u002Fetc\u002Fconfig\")\n\n    \u002F\u002F Проверка + доступ к полям:\n    var pathErr *PathError\n    if errors.As(err, &pathErr) {\n        fmt.Println(pathErr.Path) \u002F\u002F \"\u002Fetc\u002Fconfig\"\n    }\n}\n",[17,5343,5344,5350,5354,5360,5368,5376,5380,5384,5389,5399,5409,5419,5429,5433,5437,5459,5491,5495,5499,5518,5544,5548,5552,5560,5575,5579,5584,5596,5611,5623,5627],{"__ignoreMap":5},[103,5345,5346,5348],{"class":105,"line":106},[103,5347,167],{"class":116},[103,5349,170],{"class":133},[103,5351,5352],{"class":105,"line":113},[103,5353,176],{"emptyLinePlaceholder":175},[103,5355,5356,5358],{"class":105,"line":130},[103,5357,181],{"class":116},[103,5359,184],{"class":126},[103,5361,5362,5364,5366],{"class":105,"line":143},[103,5363,190],{"class":189},[103,5365,193],{"class":133},[103,5367,196],{"class":189},[103,5369,5370,5372,5374],{"class":105,"line":199},[103,5371,190],{"class":189},[103,5373,204],{"class":133},[103,5375,196],{"class":189},[103,5377,5378],{"class":105,"line":209},[103,5379,212],{"class":126},[103,5381,5382],{"class":105,"line":215},[103,5383,176],{"emptyLinePlaceholder":175},[103,5385,5386],{"class":105,"line":220},[103,5387,5388],{"class":109},"\u002F\u002F Пример: кастомный тип с контекстом (аналог os.PathError)\n",[103,5390,5391,5393,5395,5397],{"class":105,"line":232},[103,5392,117],{"class":116},[103,5394,464],{"class":133},[103,5396,467],{"class":116},[103,5398,127],{"class":126},[103,5400,5401,5404,5406],{"class":105,"line":238},[103,5402,5403],{"class":126},"    Op   ",[103,5405,834],{"class":116},[103,5407,5408],{"class":109}," \u002F\u002F \"open\", \"read\"\n",[103,5410,5411,5414,5416],{"class":105,"line":261},[103,5412,5413],{"class":126},"    Path ",[103,5415,834],{"class":116},[103,5417,5418],{"class":109}," \u002F\u002F \"\u002Fetc\u002Fconfig\"\n",[103,5420,5421,5424,5426],{"class":105,"line":266},[103,5422,5423],{"class":126},"    Err  ",[103,5425,19],{"class":116},[103,5427,5428],{"class":109},"  \u002F\u002F underlying error\n",[103,5430,5431],{"class":105,"line":272},[103,5432,146],{"class":126},[103,5434,5435],{"class":105,"line":284},[103,5436,176],{"emptyLinePlaceholder":175},[103,5438,5439,5441,5443,5445,5447,5449,5451,5453,5455,5457],{"class":105,"line":312},[103,5440,223],{"class":116},[103,5442,53],{"class":126},[103,5444,1675],{"class":658},[103,5446,1306],{"class":116},[103,5448,70],{"class":133},[103,5450,445],{"class":126},[103,5452,67],{"class":133},[103,5454,137],{"class":126},[103,5456,834],{"class":116},[103,5458,127],{"class":126},[103,5460,5461,5463,5466,5469,5472,5475,5478,5480,5483,5485,5487,5489],{"class":105,"line":317},[103,5462,721],{"class":116},[103,5464,5465],{"class":126}," e.Op ",[103,5467,5468],{"class":116},"+",[103,5470,5471],{"class":189}," \" \"",[103,5473,5474],{"class":116}," +",[103,5476,5477],{"class":126}," e.Path ",[103,5479,5468],{"class":116},[103,5481,5482],{"class":189}," \": \"",[103,5484,5474],{"class":116},[103,5486,3140],{"class":126},[103,5488,67],{"class":133},[103,5490,953],{"class":126},[103,5492,5493],{"class":105,"line":329},[103,5494,146],{"class":126},[103,5496,5497],{"class":105,"line":339},[103,5498,176],{"emptyLinePlaceholder":175},[103,5500,5501,5503,5506,5508,5510,5512,5514,5516],{"class":105,"line":506},[103,5502,223],{"class":116},[103,5504,5505],{"class":133}," openFile",[103,5507,253],{"class":126},[103,5509,659],{"class":658},[103,5511,662],{"class":116},[103,5513,445],{"class":126},[103,5515,19],{"class":116},[103,5517,127],{"class":126},[103,5519,5520,5522,5524,5526,5529,5532,5535,5537,5539,5542],{"class":105,"line":514},[103,5521,721],{"class":116},[103,5523,1385],{"class":116},[103,5525,70],{"class":133},[103,5527,5528],{"class":126},"{Op: ",[103,5530,5531],{"class":189},"\"open\"",[103,5533,5534],{"class":126},", Path: path, Err: errors.",[103,5536,250],{"class":133},[103,5538,253],{"class":126},[103,5540,5541],{"class":189},"\"no such file\"",[103,5543,3187],{"class":126},[103,5545,5546],{"class":105,"line":523},[103,5547,146],{"class":126},[103,5549,5550],{"class":105,"line":528},[103,5551,176],{"emptyLinePlaceholder":175},[103,5553,5554,5556,5558],{"class":105,"line":533},[103,5555,223],{"class":116},[103,5557,226],{"class":133},[103,5559,229],{"class":126},[103,5561,5562,5564,5566,5568,5570,5573],{"class":105,"line":542},[103,5563,946],{"class":126},[103,5565,244],{"class":116},[103,5567,5505],{"class":133},[103,5569,253],{"class":126},[103,5571,5572],{"class":189},"\"\u002Fetc\u002Fconfig\"",[103,5574,212],{"class":126},[103,5576,5577],{"class":105,"line":548},[103,5578,176],{"emptyLinePlaceholder":175},[103,5580,5581],{"class":105,"line":570},[103,5582,5583],{"class":109},"    \u002F\u002F Проверка + доступ к полям:\n",[103,5585,5586,5588,5591,5593],{"class":105,"line":580},[103,5587,3262],{"class":116},[103,5589,5590],{"class":126}," pathErr ",[103,5592,1306],{"class":116},[103,5594,5595],{"class":133},"PathError\n",[103,5597,5598,5600,5602,5604,5606,5608],{"class":105,"line":590},[103,5599,680],{"class":116},[103,5601,247],{"class":126},[103,5603,3279],{"class":133},[103,5605,4584],{"class":126},[103,5607,3285],{"class":116},[103,5609,5610],{"class":126},"pathErr) {\n",[103,5612,5613,5615,5617,5620],{"class":105,"line":1375},[103,5614,3293],{"class":126},[103,5616,323],{"class":133},[103,5618,5619],{"class":126},"(pathErr.Path) ",[103,5621,5622],{"class":109},"\u002F\u002F \"\u002Fetc\u002Fconfig\"\n",[103,5624,5625],{"class":105,"line":1380},[103,5626,716],{"class":126},[103,5628,5629],{"class":105,"line":1401},[103,5630,146],{"class":126},[148,5632,5633],{},"Неудобно складывать Op, Path в текст ошибки и потом парсить обратно. Проще — отдельный тип.",[90,5635,5637],{"id":5636},"оба-создают-зависимость","Оба создают зависимость",[95,5639,5641],{"className":97,"code":5640,"language":99,"meta":5,"style":5},"\u002F\u002F Концептуальный пример — myapp\u002Fstorage не существует в Playground.\n\u002F\u002F Суть: для sentinel и кастомного типа из другого пакета\n\u002F\u002F нужен импорт этого пакета.\n\u002F\u002F\n\u002F\u002F import \"myapp\u002Fstorage\"\n\u002F\u002F\n\u002F\u002F if errors.Is(err, storage.ErrNotFound) { ... }\n\u002F\u002F\n\u002F\u002F var dbErr *storage.DatabaseError\n\u002F\u002F if errors.As(err, &dbErr) { ... }\n",[17,5642,5643,5648,5653,5658,5663,5668,5672,5677,5681,5686],{"__ignoreMap":5},[103,5644,5645],{"class":105,"line":106},[103,5646,5647],{"class":109},"\u002F\u002F Концептуальный пример — myapp\u002Fstorage не существует в Playground.\n",[103,5649,5650],{"class":105,"line":113},[103,5651,5652],{"class":109},"\u002F\u002F Суть: для sentinel и кастомного типа из другого пакета\n",[103,5654,5655],{"class":105,"line":130},[103,5656,5657],{"class":109},"\u002F\u002F нужен импорт этого пакета.\n",[103,5659,5660],{"class":105,"line":143},[103,5661,5662],{"class":109},"\u002F\u002F\n",[103,5664,5665],{"class":105,"line":199},[103,5666,5667],{"class":109},"\u002F\u002F import \"myapp\u002Fstorage\"\n",[103,5669,5670],{"class":105,"line":209},[103,5671,5662],{"class":109},[103,5673,5674],{"class":105,"line":215},[103,5675,5676],{"class":109},"\u002F\u002F if errors.Is(err, storage.ErrNotFound) { ... }\n",[103,5678,5679],{"class":105,"line":220},[103,5680,5662],{"class":109},[103,5682,5683],{"class":105,"line":232},[103,5684,5685],{"class":109},"\u002F\u002F var dbErr *storage.DatabaseError\n",[103,5687,5688],{"class":105,"line":238},[103,5689,5690],{"class":109},"\u002F\u002F if errors.As(err, &dbErr) { ... }\n",[90,5692,5694],{"id":5693},"проверка-по-поведению-разрыв-зависимости","Проверка по поведению — разрыв зависимости",[95,5696,5698],{"className":97,"code":5697,"language":99,"meta":5,"style":5},"package main\n\nimport \"fmt\"\n\n\u002F\u002F Пакет storage — ошибка с поведением\ntype FSError struct{ path string }\n\nfunc (e *FSError) Error() string { return \"fs: \" + e.path }\nfunc (e *FSError) Path() string  { return e.path }\n\n\u002F\u002F Другой пакет — НЕ импортирует storage!\nfunc isPathError(err error) bool {\n    _, ok := err.(interface{ Path() string }) \u002F\u002F анонимный интерфейс\n    return ok\n}\n\nfunc main() {\n    err := &FSError{path: \"\u002Ftmp\u002Fdata\"}\n    fmt.Println(isPathError(err))              \u002F\u002F true\n    fmt.Println(isPathError(fmt.Errorf(\"x\"))) \u002F\u002F false\n}\n",[17,5699,5700,5706,5710,5720,5724,5729,5745,5749,5782,5810,5814,5819,5839,5867,5874,5878,5882,5890,5908,5924,5949],{"__ignoreMap":5},[103,5701,5702,5704],{"class":105,"line":106},[103,5703,167],{"class":116},[103,5705,170],{"class":133},[103,5707,5708],{"class":105,"line":113},[103,5709,176],{"emptyLinePlaceholder":175},[103,5711,5712,5714,5716,5718],{"class":105,"line":130},[103,5713,181],{"class":116},[103,5715,1644],{"class":189},[103,5717,204],{"class":133},[103,5719,196],{"class":189},[103,5721,5722],{"class":105,"line":143},[103,5723,176],{"emptyLinePlaceholder":175},[103,5725,5726],{"class":105,"line":199},[103,5727,5728],{"class":109},"\u002F\u002F Пакет storage — ошибка с поведением\n",[103,5730,5731,5733,5736,5738,5741,5743],{"class":105,"line":209},[103,5732,117],{"class":116},[103,5734,5735],{"class":133}," FSError",[103,5737,467],{"class":116},[103,5739,5740],{"class":126},"{ path ",[103,5742,834],{"class":116},[103,5744,1219],{"class":126},[103,5746,5747],{"class":105,"line":215},[103,5748,176],{"emptyLinePlaceholder":175},[103,5750,5751,5753,5755,5757,5759,5762,5764,5766,5768,5770,5772,5774,5777,5779],{"class":105,"line":220},[103,5752,223],{"class":116},[103,5754,53],{"class":126},[103,5756,1675],{"class":658},[103,5758,1306],{"class":116},[103,5760,5761],{"class":133},"FSError",[103,5763,445],{"class":126},[103,5765,67],{"class":133},[103,5767,137],{"class":126},[103,5769,834],{"class":116},[103,5771,904],{"class":126},[103,5773,907],{"class":116},[103,5775,5776],{"class":189}," \"fs: \"",[103,5778,5474],{"class":116},[103,5780,5781],{"class":126}," e.path }\n",[103,5783,5784,5786,5788,5790,5792,5794,5796,5799,5801,5803,5806,5808],{"class":105,"line":232},[103,5785,223],{"class":116},[103,5787,53],{"class":126},[103,5789,1675],{"class":658},[103,5791,1306],{"class":116},[103,5793,5761],{"class":133},[103,5795,445],{"class":126},[103,5797,5798],{"class":133},"Path",[103,5800,137],{"class":126},[103,5802,834],{"class":116},[103,5804,5805],{"class":126},"  { ",[103,5807,907],{"class":116},[103,5809,5781],{"class":126},[103,5811,5812],{"class":105,"line":238},[103,5813,176],{"emptyLinePlaceholder":175},[103,5815,5816],{"class":105,"line":261},[103,5817,5818],{"class":109},"\u002F\u002F Другой пакет — НЕ импортирует storage!\n",[103,5820,5821,5823,5826,5828,5830,5832,5834,5837],{"class":105,"line":266},[103,5822,223],{"class":116},[103,5824,5825],{"class":133}," isPathError",[103,5827,253],{"class":126},[103,5829,52],{"class":658},[103,5831,120],{"class":116},[103,5833,445],{"class":126},[103,5835,5836],{"class":116},"bool",[103,5838,127],{"class":126},[103,5840,5841,5844,5846,5849,5852,5855,5857,5859,5861,5864],{"class":105,"line":272},[103,5842,5843],{"class":126},"    _, ok ",[103,5845,244],{"class":116},[103,5847,5848],{"class":126}," err.(",[103,5850,5851],{"class":116},"interface",[103,5853,5854],{"class":126},"{ ",[103,5856,5798],{"class":133},[103,5858,137],{"class":126},[103,5860,834],{"class":116},[103,5862,5863],{"class":126}," }) ",[103,5865,5866],{"class":109},"\u002F\u002F анонимный интерфейс\n",[103,5868,5869,5871],{"class":105,"line":284},[103,5870,721],{"class":116},[103,5872,5873],{"class":126}," ok\n",[103,5875,5876],{"class":105,"line":312},[103,5877,146],{"class":126},[103,5879,5880],{"class":105,"line":317},[103,5881,176],{"emptyLinePlaceholder":175},[103,5883,5884,5886,5888],{"class":105,"line":329},[103,5885,223],{"class":116},[103,5887,226],{"class":133},[103,5889,229],{"class":126},[103,5891,5892,5894,5896,5898,5900,5903,5906],{"class":105,"line":339},[103,5893,946],{"class":126},[103,5895,244],{"class":116},[103,5897,1385],{"class":116},[103,5899,5761],{"class":133},[103,5901,5902],{"class":126},"{path: ",[103,5904,5905],{"class":189},"\"\u002Ftmp\u002Fdata\"",[103,5907,146],{"class":126},[103,5909,5910,5912,5914,5916,5919,5922],{"class":105,"line":506},[103,5911,320],{"class":126},[103,5913,323],{"class":133},[103,5915,253],{"class":126},[103,5917,5918],{"class":133},"isPathError",[103,5920,5921],{"class":126},"(err))              ",[103,5923,1940],{"class":109},[103,5925,5926,5928,5930,5932,5934,5937,5939,5941,5944,5947],{"class":105,"line":514},[103,5927,320],{"class":126},[103,5929,323],{"class":133},[103,5931,253],{"class":126},[103,5933,5918],{"class":133},[103,5935,5936],{"class":126},"(fmt.",[103,5938,295],{"class":133},[103,5940,253],{"class":126},[103,5942,5943],{"class":189},"\"x\"",[103,5945,5946],{"class":126},"))) ",[103,5948,2998],{"class":109},[103,5950,5951],{"class":105,"line":523},[103,5952,146],{"class":126},[148,5954,5955],{},"Программист должен знать поведение (какой метод проверять), но коду не нужен импорт. Для исключительных случаев, когда важно разорвать зависимость.",[85,5957,5959],{"id":5958},"совместимость-и-контракты","Совместимость и контракты",[90,5961,5963],{"id":5962},"ошибки-публичное-api","Ошибки = публичное API",[148,5965,5966],{},"Публичные sentinel'ы и типы ошибок — такая же часть API, как функции и структуры. Менять их — breaking change. Относиться бережно.",[82,5968],{},[11,5970,5971,5981,5984,5987,5996],{},[14,5972,5973,5976,5977,5980],{},[17,5974,5975],{},"errors.Join"," (Go 1.20+) — стандартный способ объединить ",[39,5978,5979],{},"список ошибок",", когда одной недостаточно",[14,5982,5983],{},"сценарий: несколько независимых ветвей могут давать ошибки (обработка среза, параллельные запросы)",[14,5985,5986],{},"возвращает nil если все переданные ошибки nil — удобно при накоплении",[14,5988,5989,5990,57,5992,57,5994],{},"полностью совместим с ",[17,5991,1094],{},[17,5993,2887],{},[17,5995,2104],{},[14,5997,5998,5999,6002],{},"компактнее, чем возвращать ",[17,6000,6001],{},"(error, error, error)"," и проверять каждую",[82,6004],{},[90,6006,6008],{"id":6007},"зачем","Зачем",[95,6010,6012],{"className":97,"code":6011,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc process(item string) error {\n    if item == \"bad\" {\n        return fmt.Errorf(\"failed: %s\", item)\n    }\n    return nil\n}\n\n\u002F\u002F Обрабатываем срез — на каждом элементе может быть сбой\nfunc ProcessAll(items []string) error {\n    var errs []error\n    for _, item := range items {\n        if err := process(item); err != nil {\n            errs = append(errs, err)\n        }\n    }\n    return errors.Join(errs...) \u002F\u002F nil если срез пуст, иначе объединённая ошибка\n}\n\nfunc main() {\n    err := ProcessAll([]string{\"ok\", \"bad\", \"also-ok\", \"bad\"})\n    fmt.Println(err)\n}\n",[17,6013,6014,6020,6024,6030,6038,6046,6050,6054,6073,6087,6107,6111,6117,6121,6125,6130,6152,6161,6177,6196,6209,6213,6217,6237,6241,6245,6253,6289,6298],{"__ignoreMap":5},[103,6015,6016,6018],{"class":105,"line":106},[103,6017,167],{"class":116},[103,6019,170],{"class":133},[103,6021,6022],{"class":105,"line":113},[103,6023,176],{"emptyLinePlaceholder":175},[103,6025,6026,6028],{"class":105,"line":130},[103,6027,181],{"class":116},[103,6029,184],{"class":126},[103,6031,6032,6034,6036],{"class":105,"line":143},[103,6033,190],{"class":189},[103,6035,193],{"class":133},[103,6037,196],{"class":189},[103,6039,6040,6042,6044],{"class":105,"line":199},[103,6041,190],{"class":189},[103,6043,204],{"class":133},[103,6045,196],{"class":189},[103,6047,6048],{"class":105,"line":209},[103,6049,212],{"class":126},[103,6051,6052],{"class":105,"line":215},[103,6053,176],{"emptyLinePlaceholder":175},[103,6055,6056,6058,6060,6062,6065,6067,6069,6071],{"class":105,"line":220},[103,6057,223],{"class":116},[103,6059,4853],{"class":133},[103,6061,253],{"class":126},[103,6063,6064],{"class":658},"item",[103,6066,662],{"class":116},[103,6068,445],{"class":126},[103,6070,19],{"class":116},[103,6072,127],{"class":126},[103,6074,6075,6077,6080,6082,6085],{"class":105,"line":232},[103,6076,680],{"class":116},[103,6078,6079],{"class":126}," item ",[103,6081,686],{"class":116},[103,6083,6084],{"class":189}," \"bad\"",[103,6086,127],{"class":126},[103,6088,6089,6091,6093,6095,6097,6100,6102,6104],{"class":105,"line":238},[103,6090,696],{"class":116},[103,6092,292],{"class":126},[103,6094,295],{"class":133},[103,6096,253],{"class":126},[103,6098,6099],{"class":189},"\"failed: ",[103,6101,2494],{"class":280},[103,6103,1921],{"class":189},[103,6105,6106],{"class":126},", item)\n",[103,6108,6109],{"class":105,"line":261},[103,6110,716],{"class":126},[103,6112,6113,6115],{"class":105,"line":266},[103,6114,721],{"class":116},[103,6116,985],{"class":280},[103,6118,6119],{"class":105,"line":272},[103,6120,146],{"class":126},[103,6122,6123],{"class":105,"line":284},[103,6124,176],{"emptyLinePlaceholder":175},[103,6126,6127],{"class":105,"line":312},[103,6128,6129],{"class":109},"\u002F\u002F Обрабатываем срез — на каждом элементе может быть сбой\n",[103,6131,6132,6134,6137,6139,6142,6144,6146,6148,6150],{"class":105,"line":317},[103,6133,223],{"class":116},[103,6135,6136],{"class":133}," ProcessAll",[103,6138,253],{"class":126},[103,6140,6141],{"class":658},"items",[103,6143,724],{"class":126},[103,6145,834],{"class":116},[103,6147,445],{"class":126},[103,6149,19],{"class":116},[103,6151,127],{"class":126},[103,6153,6154,6156,6159],{"class":105,"line":329},[103,6155,3262],{"class":116},[103,6157,6158],{"class":126}," errs []",[103,6160,484],{"class":116},[103,6162,6163,6166,6169,6171,6174],{"class":105,"line":339},[103,6164,6165],{"class":116},"    for",[103,6167,6168],{"class":126}," _, item ",[103,6170,244],{"class":116},[103,6172,6173],{"class":116}," range",[103,6175,6176],{"class":126}," items {\n",[103,6178,6179,6181,6183,6185,6187,6190,6192,6194],{"class":105,"line":506},[103,6180,4696],{"class":116},[103,6182,960],{"class":126},[103,6184,244],{"class":116},[103,6186,4853],{"class":133},[103,6188,6189],{"class":126},"(item); err ",[103,6191,963],{"class":116},[103,6193,699],{"class":280},[103,6195,127],{"class":126},[103,6197,6198,6201,6203,6206],{"class":105,"line":514},[103,6199,6200],{"class":126},"            errs ",[103,6202,409],{"class":116},[103,6204,6205],{"class":133}," append",[103,6207,6208],{"class":126},"(errs, err)\n",[103,6210,6211],{"class":105,"line":523},[103,6212,4733],{"class":126},[103,6214,6215],{"class":105,"line":528},[103,6216,716],{"class":126},[103,6218,6219,6221,6223,6226,6229,6232,6234],{"class":105,"line":533},[103,6220,721],{"class":116},[103,6222,247],{"class":126},[103,6224,6225],{"class":133},"Join",[103,6227,6228],{"class":126},"(errs",[103,6230,6231],{"class":116},"...",[103,6233,445],{"class":126},[103,6235,6236],{"class":109},"\u002F\u002F nil если срез пуст, иначе объединённая ошибка\n",[103,6238,6239],{"class":105,"line":542},[103,6240,146],{"class":126},[103,6242,6243],{"class":105,"line":548},[103,6244,176],{"emptyLinePlaceholder":175},[103,6246,6247,6249,6251],{"class":105,"line":570},[103,6248,223],{"class":116},[103,6250,226],{"class":133},[103,6252,229],{"class":126},[103,6254,6255,6257,6259,6261,6264,6266,6269,6272,6274,6277,6279,6282,6284,6286],{"class":105,"line":580},[103,6256,946],{"class":126},[103,6258,244],{"class":116},[103,6260,6136],{"class":133},[103,6262,6263],{"class":126},"([]",[103,6265,834],{"class":116},[103,6267,6268],{"class":126},"{",[103,6270,6271],{"class":189},"\"ok\"",[103,6273,57],{"class":126},[103,6275,6276],{"class":189},"\"bad\"",[103,6278,57],{"class":126},[103,6280,6281],{"class":189},"\"also-ok\"",[103,6283,57],{"class":126},[103,6285,6276],{"class":189},[103,6287,6288],{"class":126},"})\n",[103,6290,6291,6293,6295],{"class":105,"line":590},[103,6292,320],{"class":126},[103,6294,323],{"class":133},[103,6296,6297],{"class":126},"(err)\n",[103,6299,6300],{"class":105,"line":1375},[103,6301,146],{"class":126},[148,6303,6304],{},"Не хотим останавливаться на первой ошибке — обрабатываем весь срез, собираем все ошибки.",[148,6306,6307,6309],{},[17,6308,5975],{}," возвращает nil если все переданные ошибки nil. Если хотя бы одна не nil — возвращает объединённую ошибку.",[90,6311,6313],{"id":6312},"совместимость-с-isasunwrap","Совместимость с Is\u002FAs\u002FUnwrap",[95,6315,6317],{"className":97,"code":6316,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc main() {\n    err1 := errors.New(\"db timeout\")\n    err2 := errors.New(\"cache miss\")\n    var ErrFatal = errors.New(\"fatal\")\n\n    \u002F\u002F errors.Join (Go 1.20+) — объединяет несколько ошибок\n    multi := errors.Join(err1, err2, ErrFatal)\n\n    \u002F\u002F Оборачивание тоже работает\n    wrapped := fmt.Errorf(\"batch failed: %w\", multi)\n\n    \u002F\u002F Is\u002FAs проходят по всему списку\n    fmt.Println(errors.Is(wrapped, ErrFatal)) \u002F\u002F true\n}\n",[17,6318,6319,6325,6329,6335,6343,6351,6355,6359,6367,6384,6401,6421,6425,6430,6444,6448,6453,6475,6479,6484,6499],{"__ignoreMap":5},[103,6320,6321,6323],{"class":105,"line":106},[103,6322,167],{"class":116},[103,6324,170],{"class":133},[103,6326,6327],{"class":105,"line":113},[103,6328,176],{"emptyLinePlaceholder":175},[103,6330,6331,6333],{"class":105,"line":130},[103,6332,181],{"class":116},[103,6334,184],{"class":126},[103,6336,6337,6339,6341],{"class":105,"line":143},[103,6338,190],{"class":189},[103,6340,193],{"class":133},[103,6342,196],{"class":189},[103,6344,6345,6347,6349],{"class":105,"line":199},[103,6346,190],{"class":189},[103,6348,204],{"class":133},[103,6350,196],{"class":189},[103,6352,6353],{"class":105,"line":209},[103,6354,212],{"class":126},[103,6356,6357],{"class":105,"line":215},[103,6358,176],{"emptyLinePlaceholder":175},[103,6360,6361,6363,6365],{"class":105,"line":220},[103,6362,223],{"class":116},[103,6364,226],{"class":133},[103,6366,229],{"class":126},[103,6368,6369,6371,6373,6375,6377,6379,6382],{"class":105,"line":232},[103,6370,241],{"class":126},[103,6372,244],{"class":116},[103,6374,247],{"class":126},[103,6376,250],{"class":133},[103,6378,253],{"class":126},[103,6380,6381],{"class":189},"\"db timeout\"",[103,6383,212],{"class":126},[103,6385,6386,6388,6390,6392,6394,6396,6399],{"class":105,"line":238},[103,6387,287],{"class":126},[103,6389,244],{"class":116},[103,6391,247],{"class":126},[103,6393,250],{"class":133},[103,6395,253],{"class":126},[103,6397,6398],{"class":189},"\"cache miss\"",[103,6400,212],{"class":126},[103,6402,6403,6405,6408,6410,6412,6414,6416,6419],{"class":105,"line":261},[103,6404,3262],{"class":116},[103,6406,6407],{"class":126}," ErrFatal ",[103,6409,409],{"class":116},[103,6411,247],{"class":126},[103,6413,250],{"class":133},[103,6415,253],{"class":126},[103,6417,6418],{"class":189},"\"fatal\"",[103,6420,212],{"class":126},[103,6422,6423],{"class":105,"line":266},[103,6424,176],{"emptyLinePlaceholder":175},[103,6426,6427],{"class":105,"line":272},[103,6428,6429],{"class":109},"    \u002F\u002F errors.Join (Go 1.20+) — объединяет несколько ошибок\n",[103,6431,6432,6435,6437,6439,6441],{"class":105,"line":284},[103,6433,6434],{"class":126},"    multi ",[103,6436,244],{"class":116},[103,6438,247],{"class":126},[103,6440,6225],{"class":133},[103,6442,6443],{"class":126},"(err1, err2, ErrFatal)\n",[103,6445,6446],{"class":105,"line":312},[103,6447,176],{"emptyLinePlaceholder":175},[103,6449,6450],{"class":105,"line":317},[103,6451,6452],{"class":109},"    \u002F\u002F Оборачивание тоже работает\n",[103,6454,6455,6457,6459,6461,6463,6465,6468,6470,6472],{"class":105,"line":329},[103,6456,1904],{"class":126},[103,6458,244],{"class":116},[103,6460,292],{"class":126},[103,6462,295],{"class":133},[103,6464,253],{"class":126},[103,6466,6467],{"class":189},"\"batch failed: ",[103,6469,1918],{"class":280},[103,6471,1921],{"class":189},[103,6473,6474],{"class":126},", multi)\n",[103,6476,6477],{"class":105,"line":339},[103,6478,176],{"emptyLinePlaceholder":175},[103,6480,6481],{"class":105,"line":506},[103,6482,6483],{"class":109},"    \u002F\u002F Is\u002FAs проходят по всему списку\n",[103,6485,6486,6488,6490,6492,6494,6497],{"class":105,"line":514},[103,6487,320],{"class":126},[103,6489,323],{"class":133},[103,6491,555],{"class":126},[103,6493,1335],{"class":133},[103,6495,6496],{"class":126},"(wrapped, ErrFatal)) ",[103,6498,1940],{"class":109},[103,6500,6501],{"class":105,"line":523},[103,6502,146],{"class":126},[148,6504,6505,6507,6508,2368],{},[17,6506,1094],{}," при поиске проходит по всему списку ошибок внутри ",[17,6509,5975],{},[85,6511,6513],{"id":6512},"несколько-ошибок-и-стектрейсы","Несколько ошибок и стектрейсы",[90,6515,6517],{"id":6516},"почему-не-error-error-error","Почему не (error, error, error)",[95,6519,6521],{"className":97,"code":6520,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc step1() error { return errors.New(\"step1 failed\") }\nfunc step2() error { return nil }\n\n\u002F\u002F ❌ загромождает — на каждом уровне проброса проверяй все три\nfunc DoWorkBad() (error, error, error) { return step1(), step2(), nil }\n\n\u002F\u002F ✅ одна ошибка — внутри список (errors.Join, Go 1.20+)\nfunc DoWork() error {\n    return errors.Join(step1(), step2())\n}\n\nfunc main() {\n    fmt.Println(DoWork())\n}\n",[17,6522,6523,6529,6533,6539,6547,6555,6559,6563,6589,6608,6612,6617,6656,6660,6665,6678,6697,6701,6705,6713,6726],{"__ignoreMap":5},[103,6524,6525,6527],{"class":105,"line":106},[103,6526,167],{"class":116},[103,6528,170],{"class":133},[103,6530,6531],{"class":105,"line":113},[103,6532,176],{"emptyLinePlaceholder":175},[103,6534,6535,6537],{"class":105,"line":130},[103,6536,181],{"class":116},[103,6538,184],{"class":126},[103,6540,6541,6543,6545],{"class":105,"line":143},[103,6542,190],{"class":189},[103,6544,193],{"class":133},[103,6546,196],{"class":189},[103,6548,6549,6551,6553],{"class":105,"line":199},[103,6550,190],{"class":189},[103,6552,204],{"class":133},[103,6554,196],{"class":189},[103,6556,6557],{"class":105,"line":209},[103,6558,212],{"class":126},[103,6560,6561],{"class":105,"line":215},[103,6562,176],{"emptyLinePlaceholder":175},[103,6564,6565,6567,6570,6572,6574,6576,6578,6580,6582,6584,6587],{"class":105,"line":220},[103,6566,223],{"class":116},[103,6568,6569],{"class":133}," step1",[103,6571,137],{"class":126},[103,6573,19],{"class":116},[103,6575,904],{"class":126},[103,6577,907],{"class":116},[103,6579,247],{"class":126},[103,6581,250],{"class":133},[103,6583,253],{"class":126},[103,6585,6586],{"class":189},"\"step1 failed\"",[103,6588,919],{"class":126},[103,6590,6591,6593,6596,6598,6600,6602,6604,6606],{"class":105,"line":232},[103,6592,223],{"class":116},[103,6594,6595],{"class":133}," step2",[103,6597,137],{"class":126},[103,6599,19],{"class":116},[103,6601,904],{"class":126},[103,6603,907],{"class":116},[103,6605,699],{"class":280},[103,6607,1219],{"class":126},[103,6609,6610],{"class":105,"line":238},[103,6611,176],{"emptyLinePlaceholder":175},[103,6613,6614],{"class":105,"line":261},[103,6615,6616],{"class":109},"\u002F\u002F ❌ загромождает — на каждом уровне проброса проверяй все три\n",[103,6618,6619,6621,6624,6627,6629,6631,6633,6635,6637,6640,6642,6644,6647,6650,6652,6654],{"class":105,"line":266},[103,6620,223],{"class":116},[103,6622,6623],{"class":133}," DoWorkBad",[103,6625,6626],{"class":126},"() (",[103,6628,19],{"class":116},[103,6630,57],{"class":126},[103,6632,19],{"class":116},[103,6634,57],{"class":126},[103,6636,19],{"class":116},[103,6638,6639],{"class":126},") { ",[103,6641,907],{"class":116},[103,6643,6569],{"class":133},[103,6645,6646],{"class":126},"(), ",[103,6648,6649],{"class":133},"step2",[103,6651,6646],{"class":126},[103,6653,2198],{"class":280},[103,6655,1219],{"class":126},[103,6657,6658],{"class":105,"line":272},[103,6659,176],{"emptyLinePlaceholder":175},[103,6661,6662],{"class":105,"line":284},[103,6663,6664],{"class":109},"\u002F\u002F ✅ одна ошибка — внутри список (errors.Join, Go 1.20+)\n",[103,6666,6667,6669,6672,6674,6676],{"class":105,"line":312},[103,6668,223],{"class":116},[103,6670,6671],{"class":133}," DoWork",[103,6673,137],{"class":126},[103,6675,19],{"class":116},[103,6677,127],{"class":126},[103,6679,6680,6682,6684,6686,6688,6691,6693,6695],{"class":105,"line":317},[103,6681,721],{"class":116},[103,6683,247],{"class":126},[103,6685,6225],{"class":133},[103,6687,253],{"class":126},[103,6689,6690],{"class":133},"step1",[103,6692,6646],{"class":126},[103,6694,6649],{"class":133},[103,6696,1049],{"class":126},[103,6698,6699],{"class":105,"line":329},[103,6700,146],{"class":126},[103,6702,6703],{"class":105,"line":339},[103,6704,176],{"emptyLinePlaceholder":175},[103,6706,6707,6709,6711],{"class":105,"line":506},[103,6708,223],{"class":116},[103,6710,226],{"class":133},[103,6712,229],{"class":126},[103,6714,6715,6717,6719,6721,6724],{"class":105,"line":514},[103,6716,320],{"class":126},[103,6718,323],{"class":133},[103,6720,253],{"class":126},[103,6722,6723],{"class":133},"DoWork",[103,6725,1049],{"class":126},[103,6727,6728],{"class":105,"line":523},[103,6729,146],{"class":126},[82,6731],{},[11,6733,6734,6744,6751,6758],{},[14,6735,6736,6737,6739,6740,6743],{},"стандартный пакет ",[17,6738,193],{}," ",[39,6741,6742],{},"не даёт"," стектрейс",[14,6745,6746,6747,6750],{},"пакет ",[17,6748,6749],{},"github.com\u002Fpkg\u002Ferrors"," — сохраняет стек при создании ошибки",[14,6752,6753,6754,6757],{},"снятие стектрейса — ",[39,6755,6756],{},"не бесплатно",", бенчмарк показывает существенный оверхед",[14,6759,6760],{},"не злоупотреблять в горячих местах и логах — логи стоят дорого",[82,6762],{},[90,6764,6766],{"id":6765},"стандартный-пакет-без-стектрейса","Стандартный пакет — без стектрейса",[95,6768,6770],{"className":97,"code":6769,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nfunc main() {\n    err := errors.New(\"something failed\")\n    fmt.Println(err)        \u002F\u002F something failed\n    fmt.Println(err.Error()) \u002F\u002F то же самое — только текст, без стека\n}\n",[17,6771,6772,6778,6782,6788,6796,6804,6808,6812,6820,6837,6849,6866],{"__ignoreMap":5},[103,6773,6774,6776],{"class":105,"line":106},[103,6775,167],{"class":116},[103,6777,170],{"class":133},[103,6779,6780],{"class":105,"line":113},[103,6781,176],{"emptyLinePlaceholder":175},[103,6783,6784,6786],{"class":105,"line":130},[103,6785,181],{"class":116},[103,6787,184],{"class":126},[103,6789,6790,6792,6794],{"class":105,"line":143},[103,6791,190],{"class":189},[103,6793,193],{"class":133},[103,6795,196],{"class":189},[103,6797,6798,6800,6802],{"class":105,"line":199},[103,6799,190],{"class":189},[103,6801,204],{"class":133},[103,6803,196],{"class":189},[103,6805,6806],{"class":105,"line":209},[103,6807,212],{"class":126},[103,6809,6810],{"class":105,"line":215},[103,6811,176],{"emptyLinePlaceholder":175},[103,6813,6814,6816,6818],{"class":105,"line":220},[103,6815,223],{"class":116},[103,6817,226],{"class":133},[103,6819,229],{"class":126},[103,6821,6822,6824,6826,6828,6830,6832,6835],{"class":105,"line":232},[103,6823,946],{"class":126},[103,6825,244],{"class":116},[103,6827,247],{"class":126},[103,6829,250],{"class":133},[103,6831,253],{"class":126},[103,6833,6834],{"class":189},"\"something failed\"",[103,6836,212],{"class":126},[103,6838,6839,6841,6843,6846],{"class":105,"line":238},[103,6840,320],{"class":126},[103,6842,323],{"class":133},[103,6844,6845],{"class":126},"(err)        ",[103,6847,6848],{"class":109},"\u002F\u002F something failed\n",[103,6850,6851,6853,6855,6858,6860,6863],{"class":105,"line":261},[103,6852,320],{"class":126},[103,6854,323],{"class":133},[103,6856,6857],{"class":126},"(err.",[103,6859,67],{"class":133},[103,6861,6862],{"class":126},"()) ",[103,6864,6865],{"class":109},"\u002F\u002F то же самое — только текст, без стека\n",[103,6867,6868],{"class":105,"line":266},[103,6869,146],{"class":126},[148,6871,6872,6873,6876],{},"Стандартный ",[17,6874,6875],{},"errors.New"," создаёт ошибку только с текстом — никакого стека вызовов.",[90,6878,6880],{"id":6879},"githubcompkgerrors-со-стектрейсом","github.com\u002Fpkg\u002Ferrors — со стектрейсом",[95,6882,6885],{"className":6883,"code":6884,"language":4811},[4809],"\u002F\u002F github.com\u002Fpkg\u002Ferrors — внешний пакет, недоступен в Playground.\n\u002F\u002F Пример показывает концепцию — реальный запуск требует go get.\n\u002F\u002F\n\u002F\u002F pkgerrors.New(\"something failed\") сохраняет стек вызова.\n\u002F\u002F fmt.Printf(\"%+v\\n\", err) выводит:\n\u002F\u002F   something failed\n\u002F\u002F   main.process\n\u002F\u002F       \u002Fapp\u002Fmain.go:15\n\u002F\u002F   main.main\n\u002F\u002F       \u002Fapp\u002Fmain.go:8\n",[17,6886,6884],{"__ignoreMap":5},[148,6888,6889,6892,6893,6896],{},[17,6890,6891],{},"%+v"," — специальный формат для ",[17,6894,6895],{},"pkg\u002Ferrors",", выводит текст ошибки + полный стектрейс с именами функций и номерами строк.",[90,6898,6900],{"id":6899},"оверхед","Оверхед",[95,6902,6905],{"className":6903,"code":6904,"language":4811},[4809],"BenchmarkStandardError    ~50 ns\u002Fop     0 allocs\nBenchmarkPkgErrors        ~2000 ns\u002Fop   3 allocs\n",[17,6906,6904],{"__ignoreMap":5},[148,6908,6909,6910,6912,6913,6916],{},"Снять стектрейс ≈ в 40 раз дороже стандартного ",[17,6911,6875],{},". Причина: ",[17,6914,6915],{},"runtime.Callers"," — проход по стеку, аллокации.",[90,6918,6920],{"id":6919},"когда-использовать","Когда использовать",[148,6922,6923,6924,6927],{},"Стектрейс полезен для ",[39,6925,6926],{},"отладки",", но не нужен всегда. Если в горячем цикле создаёте ошибки со стектрейсом — это оверхед. Если логируете стектрейсы в каждом безобидном логе — логи разрастаются и стоят дорого.",[82,6929],{},[85,6931,6933],{"id":6932},"практика","Практика",[6935,6936,6940,6947,6973],"quiz",{"answer":6937,"id":6938,"xp":6939},"2","funcs-errors-q1","10",[148,6941,6942,6943,6946],{},"Что должен использовать вызывающий код, если ошибка была обёрнута через ",[17,6944,6945],{},"fmt.Errorf(\"context: %w\", err)","?",[6948,6949,6950],"template",{"v-slot:options":5},[11,6951,6952,6958,6963,6968],{},[14,6953,6954,6955],{},"Сравнение ",[17,6956,6957],{},"err == target",[14,6959,6960],{},[17,6961,6962],{},"errors.Is(err, target)",[14,6964,6954,6965],{},[17,6966,6967],{},"err.Error() == target.Error()",[14,6969,6970],{},[17,6971,6972],{},"strings.Contains(err.Error(), \"...\")",[6948,6974,6975],{"v-slot:explanation":5},[148,6976,6977,6979,6980,6982],{},[17,6978,1918],{}," сохраняет цепочку ошибок. Прямое сравнение видит только внешний объект, а ",[17,6981,1094],{}," рекурсивно проходит через unwrap и находит исходную sentinel-ошибку.",[6984,6985,6989,7122],"predict",{"answer":6986,"id":6987,"xp":6988},"false\\ntrue","funcs-errors-p1","15",[6948,6990,6991],{"v-slot:code":5},[95,6992,6994],{"className":97,"code":6993,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nvar ErrNotFound = errors.New(\"not found\")\n\nfunc main() {\n    err := fmt.Errorf(\"load user: %w\", ErrNotFound)\n\n    fmt.Println(err == ErrNotFound)\n    fmt.Println(errors.Is(err, ErrNotFound))\n}\n",[17,6995,6996,7002,7006,7012,7020,7028,7032,7036,7054,7058,7066,7088,7092,7105,7118],{"__ignoreMap":5},[103,6997,6998,7000],{"class":105,"line":106},[103,6999,167],{"class":116},[103,7001,170],{"class":133},[103,7003,7004],{"class":105,"line":113},[103,7005,176],{"emptyLinePlaceholder":175},[103,7007,7008,7010],{"class":105,"line":130},[103,7009,181],{"class":116},[103,7011,184],{"class":126},[103,7013,7014,7016,7018],{"class":105,"line":143},[103,7015,190],{"class":189},[103,7017,193],{"class":133},[103,7019,196],{"class":189},[103,7021,7022,7024,7026],{"class":105,"line":199},[103,7023,190],{"class":189},[103,7025,204],{"class":133},[103,7027,196],{"class":189},[103,7029,7030],{"class":105,"line":209},[103,7031,212],{"class":126},[103,7033,7034],{"class":105,"line":215},[103,7035,176],{"emptyLinePlaceholder":175},[103,7037,7038,7040,7042,7044,7046,7048,7050,7052],{"class":105,"line":220},[103,7039,403],{"class":116},[103,7041,406],{"class":126},[103,7043,409],{"class":116},[103,7045,247],{"class":126},[103,7047,250],{"class":133},[103,7049,253],{"class":126},[103,7051,418],{"class":189},[103,7053,212],{"class":126},[103,7055,7056],{"class":105,"line":232},[103,7057,176],{"emptyLinePlaceholder":175},[103,7059,7060,7062,7064],{"class":105,"line":238},[103,7061,223],{"class":116},[103,7063,226],{"class":133},[103,7065,229],{"class":126},[103,7067,7068,7070,7072,7074,7076,7078,7081,7083,7085],{"class":105,"line":261},[103,7069,946],{"class":126},[103,7071,244],{"class":116},[103,7073,292],{"class":126},[103,7075,295],{"class":133},[103,7077,253],{"class":126},[103,7079,7080],{"class":189},"\"load user: ",[103,7082,1918],{"class":280},[103,7084,1921],{"class":189},[103,7086,7087],{"class":126},", ErrNotFound)\n",[103,7089,7090],{"class":105,"line":266},[103,7091,176],{"emptyLinePlaceholder":175},[103,7093,7094,7096,7098,7100,7102],{"class":105,"line":272},[103,7095,320],{"class":126},[103,7097,323],{"class":133},[103,7099,3500],{"class":126},[103,7101,686],{"class":116},[103,7103,7104],{"class":126}," ErrNotFound)\n",[103,7106,7107,7109,7111,7113,7115],{"class":105,"line":284},[103,7108,320],{"class":126},[103,7110,323],{"class":133},[103,7112,555],{"class":126},[103,7114,1335],{"class":133},[103,7116,7117],{"class":126},"(err, ErrNotFound))\n",[103,7119,7120],{"class":105,"line":312},[103,7121,146],{"class":126},[6948,7123,7124],{"v-slot:hint":5},[148,7125,7126,7128],{},[17,7127,2865],{}," создаёт новую ошибку-обёртку. Исходная ошибка лежит внутри цепочки unwrap.",[7130,7131,7135,7148,7428],"code-task",{"expected":7132,"id":7133,"xp":7134},"true\\nmissing rate USD\u002FRUB: rate not found","funcs-errors-ct1","25",[148,7136,7137,7138,7141,7142,7145,7146,2368],{},"Реализуй ",[17,7139,7140],{},"GetRate",": если курса нет, верни ошибку с контекстом пары и sentinel ",[17,7143,7144],{},"ErrRateNotFound",", чтобы вызывающий код мог проверить её через ",[17,7147,1094],{},[6948,7149,7150],{"v-slot:template":5},[95,7151,7153],{"className":97,"code":7152,"language":99,"meta":5,"style":5},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n)\n\nvar ErrRateNotFound = errors.New(\"rate not found\")\n\ntype Pair struct {\n    From string\n    To   string\n}\n\nfunc GetRate(rates map[Pair]float64, pair Pair) (float64, error) {\n    \u002F\u002F твой код здесь\n    return 0, nil\n}\n\nfunc main() {\n    rates := map[Pair]float64{\n        {From: \"EUR\", To: \"USD\"}: 1.08,\n    }\n\n    _, err := GetRate(rates, Pair{From: \"USD\", To: \"RUB\"})\n    fmt.Println(errors.Is(err, ErrRateNotFound))\n    fmt.Println(err)\n}\n",[17,7154,7155,7161,7165,7171,7179,7187,7191,7195,7215,7219,7230,7237,7244,7248,7252,7296,7301,7311,7315,7319,7327,7347,7370,7374,7378,7403,7416,7424],{"__ignoreMap":5},[103,7156,7157,7159],{"class":105,"line":106},[103,7158,167],{"class":116},[103,7160,170],{"class":133},[103,7162,7163],{"class":105,"line":113},[103,7164,176],{"emptyLinePlaceholder":175},[103,7166,7167,7169],{"class":105,"line":130},[103,7168,181],{"class":116},[103,7170,184],{"class":126},[103,7172,7173,7175,7177],{"class":105,"line":143},[103,7174,190],{"class":189},[103,7176,193],{"class":133},[103,7178,196],{"class":189},[103,7180,7181,7183,7185],{"class":105,"line":199},[103,7182,190],{"class":189},[103,7184,204],{"class":133},[103,7186,196],{"class":189},[103,7188,7189],{"class":105,"line":209},[103,7190,212],{"class":126},[103,7192,7193],{"class":105,"line":215},[103,7194,176],{"emptyLinePlaceholder":175},[103,7196,7197,7199,7202,7204,7206,7208,7210,7213],{"class":105,"line":220},[103,7198,403],{"class":116},[103,7200,7201],{"class":126}," ErrRateNotFound ",[103,7203,409],{"class":116},[103,7205,247],{"class":126},[103,7207,250],{"class":133},[103,7209,253],{"class":126},[103,7211,7212],{"class":189},"\"rate not found\"",[103,7214,212],{"class":126},[103,7216,7217],{"class":105,"line":232},[103,7218,176],{"emptyLinePlaceholder":175},[103,7220,7221,7223,7226,7228],{"class":105,"line":238},[103,7222,117],{"class":116},[103,7224,7225],{"class":133}," Pair",[103,7227,467],{"class":116},[103,7229,127],{"class":126},[103,7231,7232,7235],{"class":105,"line":261},[103,7233,7234],{"class":126},"    From ",[103,7236,140],{"class":116},[103,7238,7239,7242],{"class":105,"line":266},[103,7240,7241],{"class":126},"    To   ",[103,7243,140],{"class":116},[103,7245,7246],{"class":105,"line":272},[103,7247,146],{"class":126},[103,7249,7250],{"class":105,"line":284},[103,7251,176],{"emptyLinePlaceholder":175},[103,7253,7254,7256,7259,7261,7264,7267,7270,7273,7276,7279,7281,7284,7286,7288,7290,7292,7294],{"class":105,"line":312},[103,7255,223],{"class":116},[103,7257,7258],{"class":133}," GetRate",[103,7260,253],{"class":126},[103,7262,7263],{"class":658},"rates",[103,7265,7266],{"class":116}," map",[103,7268,7269],{"class":126},"[",[103,7271,7272],{"class":133},"Pair",[103,7274,7275],{"class":126},"]",[103,7277,7278],{"class":116},"float64",[103,7280,57],{"class":126},[103,7282,7283],{"class":658},"pair",[103,7285,7225],{"class":133},[103,7287,766],{"class":126},[103,7289,7278],{"class":116},[103,7291,57],{"class":126},[103,7293,19],{"class":116},[103,7295,675],{"class":126},[103,7297,7298],{"class":105,"line":317},[103,7299,7300],{"class":109},"    \u002F\u002F твой код здесь\n",[103,7302,7303,7305,7307,7309],{"class":105,"line":329},[103,7304,721],{"class":116},[103,7306,1261],{"class":280},[103,7308,57],{"class":126},[103,7310,737],{"class":280},[103,7312,7313],{"class":105,"line":339},[103,7314,146],{"class":126},[103,7316,7317],{"class":105,"line":506},[103,7318,176],{"emptyLinePlaceholder":175},[103,7320,7321,7323,7325],{"class":105,"line":514},[103,7322,223],{"class":116},[103,7324,226],{"class":133},[103,7326,229],{"class":126},[103,7328,7329,7332,7334,7336,7338,7340,7342,7344],{"class":105,"line":523},[103,7330,7331],{"class":126},"    rates ",[103,7333,244],{"class":116},[103,7335,7266],{"class":116},[103,7337,7269],{"class":126},[103,7339,7272],{"class":133},[103,7341,7275],{"class":126},[103,7343,7278],{"class":116},[103,7345,7346],{"class":126},"{\n",[103,7348,7349,7352,7355,7358,7361,7364,7367],{"class":105,"line":528},[103,7350,7351],{"class":126},"        {From: ",[103,7353,7354],{"class":189},"\"EUR\"",[103,7356,7357],{"class":126},", To: ",[103,7359,7360],{"class":189},"\"USD\"",[103,7362,7363],{"class":126},"}: ",[103,7365,7366],{"class":280},"1.08",[103,7368,7369],{"class":126},",\n",[103,7371,7372],{"class":105,"line":533},[103,7373,716],{"class":126},[103,7375,7376],{"class":105,"line":542},[103,7377,176],{"emptyLinePlaceholder":175},[103,7379,7380,7382,7384,7386,7389,7391,7394,7396,7398,7401],{"class":105,"line":548},[103,7381,2455],{"class":126},[103,7383,244],{"class":116},[103,7385,7258],{"class":133},[103,7387,7388],{"class":126},"(rates, ",[103,7390,7272],{"class":133},[103,7392,7393],{"class":126},"{From: ",[103,7395,7360],{"class":189},[103,7397,7357],{"class":126},[103,7399,7400],{"class":189},"\"RUB\"",[103,7402,6288],{"class":126},[103,7404,7405,7407,7409,7411,7413],{"class":105,"line":570},[103,7406,320],{"class":126},[103,7408,323],{"class":133},[103,7410,555],{"class":126},[103,7412,1335],{"class":133},[103,7414,7415],{"class":126},"(err, ErrRateNotFound))\n",[103,7417,7418,7420,7422],{"class":105,"line":580},[103,7419,320],{"class":126},[103,7421,323],{"class":133},[103,7423,6297],{"class":126},[103,7425,7426],{"class":105,"line":590},[103,7427,146],{"class":126},[6948,7429,7430],{"v-slot:hints":5},[11,7431,7432,7438,7447],{},[14,7433,7434,7435],{},"Проверь наличие значения через ",[17,7436,7437],{},"rate, ok := rates[pair]",[14,7439,7440,7441,7443,7444],{},"Для ошибки используй ",[17,7442,1918],{},", а текст контекста сделай человекочитаемым: ",[17,7445,7446],{},"missing rate USD\u002FRUB: rate not found",[14,7448,7449,7450],{},"Если курс найден, верни ",[17,7451,7452],{},"rate, nil",[7454,7455,7456],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}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}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":5,"searchDepth":113,"depth":113,"links":7458},[7459,7466,7473,7479,7485,7492,7500,7506,7511,7518],{"id":87,"depth":113,"text":88,"children":7460},[7461,7462,7463,7464,7465],{"id":92,"depth":130,"text":93},{"id":156,"depth":130,"text":157},{"id":347,"depth":130,"text":348},{"id":595,"depth":130,"text":596},{"id":844,"depth":130,"text":845},{"id":1103,"depth":113,"text":1104,"children":7467},[7468,7469,7470,7471,7472],{"id":1107,"depth":130,"text":1108},{"id":1494,"depth":130,"text":1495},{"id":1621,"depth":130,"text":1622},{"id":1786,"depth":130,"text":1787},{"id":1950,"depth":130,"text":1951},{"id":2009,"depth":113,"text":2010,"children":7474},[7475,7476,7477,7478],{"id":2013,"depth":130,"text":2014},{"id":2208,"depth":130,"text":2209},{"id":2371,"depth":130,"text":2372},{"id":2699,"depth":130,"text":2700},{"id":2916,"depth":113,"text":2917,"children":7480},[7481,7482,7483,7484],{"id":2920,"depth":130,"text":2921},{"id":3033,"depth":130,"text":3034},{"id":3317,"depth":130,"text":3318},{"id":3541,"depth":130,"text":3542},{"id":3723,"depth":113,"text":3724,"children":7486},[7487,7488,7489,7490,7491],{"id":3727,"depth":130,"text":3728},{"id":3852,"depth":130,"text":3853},{"id":3887,"depth":130,"text":3888},{"id":4396,"depth":130,"text":4397},{"id":4410,"depth":130,"text":4411},{"id":4639,"depth":113,"text":4640,"children":7493},[7494,7495,7496,7497,7498,7499],{"id":4643,"depth":130,"text":4644},{"id":4804,"depth":130,"text":4805},{"id":4816,"depth":130,"text":4817},{"id":4973,"depth":130,"text":4974},{"id":5079,"depth":130,"text":5080},{"id":5093,"depth":130,"text":5094},{"id":5138,"depth":113,"text":5139,"children":7501},[7502,7503,7504,7505],{"id":5142,"depth":130,"text":5143},{"id":5337,"depth":130,"text":5338},{"id":5636,"depth":130,"text":5637},{"id":5693,"depth":130,"text":5694},{"id":5958,"depth":113,"text":5959,"children":7507},[7508,7509,7510],{"id":5962,"depth":130,"text":5963},{"id":6007,"depth":130,"text":6008},{"id":6312,"depth":130,"text":6313},{"id":6512,"depth":113,"text":6513,"children":7512},[7513,7514,7515,7516,7517],{"id":6516,"depth":130,"text":6517},{"id":6765,"depth":130,"text":6766},{"id":6879,"depth":130,"text":6880},{"id":6899,"depth":130,"text":6900},{"id":6919,"depth":130,"text":6920},{"id":6932,"depth":113,"text":6933},1781458319760]