[{"data":1,"prerenderedAt":3955},["ShallowReactive",2],{"page-\u002Fgo\u002F28-exercises-and-projects":3},{"id":4,"title":5,"body":6,"description":3948,"extension":3949,"meta":3950,"navigation":72,"path":3951,"seo":3952,"stem":3953,"__hash__":3954},"content\u002Fgo\u002F28-exercises-and-projects.md","28 — Exercises & Project Ideas",{"type":7,"value":8,"toc":3942},"minimark",[9,13,17,22,27,42,3938],[10,11,5],"h1",{"id":12},"_28-exercises-project-ideas",[14,15,16],"p",{},"Practice exercises progressing from beginner to expert, covering every chapter. Set up a module and work through them.",[18,19,21],"h2",{"id":20},"beginner","Beginner",[23,24,26],"h3",{"id":25},"_1-cli-tool-with-flags","1. CLI Tool with Flags",[14,28,29,30,34,35,34,38,41],{},"Build a CLI tool that reads a CSV file, filters rows by a column value, and outputs JSON. Use ",[31,32,33],"code",{},"flag",", ",[31,36,37],{},"encoding\u002Fcsv",[31,39,40],{},"encoding\u002Fjson",".",[43,44,46,821,825,846],"code-wrapper",{"language":45},"go",[47,48,52],"pre",{"className":49,"code":50,"language":45,"meta":51,"style":51},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"encoding\u002Fcsv\"\n    \"encoding\u002Fjson\"\n    \"flag\"\n    \"fmt\"\n    \"os\"\n)\n\nfunc main() {\n    input := flag.String(\"input\", \"\", \"input CSV file path\")\n    filterCol := flag.Int(\"col\", 0, \"column index to filter on\")\n    filterVal := flag.String(\"value\", \"\", \"value to match\")\n    flag.Parse()\n\n    if *input == \"\" || *filterVal == \"\" {\n        fmt.Fprintln(os.Stderr, \"usage: filter -input file.csv -col 0 -value alice\")\n        os.Exit(2)\n    }\n\n    f, err := os.Open(*input)\n    if err != nil {\n        fmt.Fprintf(os.Stderr, \"open: %v\\n\", err)\n        os.Exit(1)\n    }\n    defer f.Close()\n\n    r := csv.NewReader(f)\n    records, err := r.ReadAll()\n    if err != nil {\n        fmt.Fprintf(os.Stderr, \"read csv: %v\\n\", err)\n        os.Exit(1)\n    }\n\n    var result []map[string]string\n    if len(records) == 0 {\n        return\n    }\n    headers := records[0]\n    for _, row := range records[1:] {\n        if len(row) > *filterCol && row[*filterCol] == *filterVal {\n            m := make(map[string]string)\n            for i, h := range headers {\n                if i \u003C len(row) {\n                    m[h] = row[i]\n                }\n            }\n            result = append(result, m)\n        }\n    }\n\n    enc := json.NewEncoder(os.Stdout)\n    enc.SetIndent(\"\", \"  \")\n    enc.Encode(result)\n}\n","",[31,53,54,67,74,84,96,105,114,124,134,140,145,157,190,221,249,261,266,299,316,332,338,343,365,381,403,417,422,436,441,458,474,487,505,518,523,528,552,570,576,581,597,618,655,680,696,713,725,731,737,751,757,762,767,784,804,815],{"__ignoreMap":51},[55,56,59,63],"span",{"class":57,"line":58},"line",1,[55,60,62],{"class":61},"svdQ7","package",[55,64,66],{"class":65},"sIsaT"," main\n",[55,68,70],{"class":57,"line":69},2,[55,71,73],{"emptyLinePlaceholder":72},true,"\n",[55,75,77,80],{"class":57,"line":76},3,[55,78,79],{"class":61},"import",[55,81,83],{"class":82},"ssxIu"," (\n",[55,85,87,91,93],{"class":57,"line":86},4,[55,88,90],{"class":89},"sJ6F3","    \"",[55,92,37],{"class":65},[55,94,95],{"class":89},"\"\n",[55,97,99,101,103],{"class":57,"line":98},5,[55,100,90],{"class":89},[55,102,40],{"class":65},[55,104,95],{"class":89},[55,106,108,110,112],{"class":57,"line":107},6,[55,109,90],{"class":89},[55,111,33],{"class":65},[55,113,95],{"class":89},[55,115,117,119,122],{"class":57,"line":116},7,[55,118,90],{"class":89},[55,120,121],{"class":65},"fmt",[55,123,95],{"class":89},[55,125,127,129,132],{"class":57,"line":126},8,[55,128,90],{"class":89},[55,130,131],{"class":65},"os",[55,133,95],{"class":89},[55,135,137],{"class":57,"line":136},9,[55,138,139],{"class":82},")\n",[55,141,143],{"class":57,"line":142},10,[55,144,73],{"emptyLinePlaceholder":72},[55,146,148,151,154],{"class":57,"line":147},11,[55,149,150],{"class":61},"func",[55,152,153],{"class":65}," main",[55,155,156],{"class":82},"() {\n",[55,158,160,163,166,169,172,175,178,180,183,185,188],{"class":57,"line":159},12,[55,161,162],{"class":82},"    input ",[55,164,165],{"class":61},":=",[55,167,168],{"class":82}," flag.",[55,170,171],{"class":65},"String",[55,173,174],{"class":82},"(",[55,176,177],{"class":89},"\"input\"",[55,179,34],{"class":82},[55,181,182],{"class":89},"\"\"",[55,184,34],{"class":82},[55,186,187],{"class":89},"\"input CSV file path\"",[55,189,139],{"class":82},[55,191,193,196,198,200,203,205,208,210,214,216,219],{"class":57,"line":192},13,[55,194,195],{"class":82},"    filterCol ",[55,197,165],{"class":61},[55,199,168],{"class":82},[55,201,202],{"class":65},"Int",[55,204,174],{"class":82},[55,206,207],{"class":89},"\"col\"",[55,209,34],{"class":82},[55,211,213],{"class":212},"snvgF","0",[55,215,34],{"class":82},[55,217,218],{"class":89},"\"column index to filter on\"",[55,220,139],{"class":82},[55,222,224,227,229,231,233,235,238,240,242,244,247],{"class":57,"line":223},14,[55,225,226],{"class":82},"    filterVal ",[55,228,165],{"class":61},[55,230,168],{"class":82},[55,232,171],{"class":65},[55,234,174],{"class":82},[55,236,237],{"class":89},"\"value\"",[55,239,34],{"class":82},[55,241,182],{"class":89},[55,243,34],{"class":82},[55,245,246],{"class":89},"\"value to match\"",[55,248,139],{"class":82},[55,250,252,255,258],{"class":57,"line":251},15,[55,253,254],{"class":82},"    flag.",[55,256,257],{"class":65},"Parse",[55,259,260],{"class":82},"()\n",[55,262,264],{"class":57,"line":263},16,[55,265,73],{"emptyLinePlaceholder":72},[55,267,269,272,275,278,281,284,287,289,292,294,296],{"class":57,"line":268},17,[55,270,271],{"class":61},"    if",[55,273,274],{"class":61}," *",[55,276,277],{"class":82},"input ",[55,279,280],{"class":61},"==",[55,282,283],{"class":89}," \"\"",[55,285,286],{"class":61}," ||",[55,288,274],{"class":61},[55,290,291],{"class":82},"filterVal ",[55,293,280],{"class":61},[55,295,283],{"class":89},[55,297,298],{"class":82}," {\n",[55,300,302,305,308,311,314],{"class":57,"line":301},18,[55,303,304],{"class":82},"        fmt.",[55,306,307],{"class":65},"Fprintln",[55,309,310],{"class":82},"(os.Stderr, ",[55,312,313],{"class":89},"\"usage: filter -input file.csv -col 0 -value alice\"",[55,315,139],{"class":82},[55,317,319,322,325,327,330],{"class":57,"line":318},19,[55,320,321],{"class":82},"        os.",[55,323,324],{"class":65},"Exit",[55,326,174],{"class":82},[55,328,329],{"class":212},"2",[55,331,139],{"class":82},[55,333,335],{"class":57,"line":334},20,[55,336,337],{"class":82},"    }\n",[55,339,341],{"class":57,"line":340},21,[55,342,73],{"emptyLinePlaceholder":72},[55,344,346,349,351,354,357,359,362],{"class":57,"line":345},22,[55,347,348],{"class":82},"    f, err ",[55,350,165],{"class":61},[55,352,353],{"class":82}," os.",[55,355,356],{"class":65},"Open",[55,358,174],{"class":82},[55,360,361],{"class":61},"*",[55,363,364],{"class":82},"input)\n",[55,366,368,370,373,376,379],{"class":57,"line":367},23,[55,369,271],{"class":61},[55,371,372],{"class":82}," err ",[55,374,375],{"class":61},"!=",[55,377,378],{"class":212}," nil",[55,380,298],{"class":82},[55,382,384,386,389,391,394,397,400],{"class":57,"line":383},24,[55,385,304],{"class":82},[55,387,388],{"class":65},"Fprintf",[55,390,310],{"class":82},[55,392,393],{"class":89},"\"open: ",[55,395,396],{"class":212},"%v\\n",[55,398,399],{"class":89},"\"",[55,401,402],{"class":82},", err)\n",[55,404,406,408,410,412,415],{"class":57,"line":405},25,[55,407,321],{"class":82},[55,409,324],{"class":65},[55,411,174],{"class":82},[55,413,414],{"class":212},"1",[55,416,139],{"class":82},[55,418,420],{"class":57,"line":419},26,[55,421,337],{"class":82},[55,423,425,428,431,434],{"class":57,"line":424},27,[55,426,427],{"class":61},"    defer",[55,429,430],{"class":82}," f.",[55,432,433],{"class":65},"Close",[55,435,260],{"class":82},[55,437,439],{"class":57,"line":438},28,[55,440,73],{"emptyLinePlaceholder":72},[55,442,444,447,449,452,455],{"class":57,"line":443},29,[55,445,446],{"class":82},"    r ",[55,448,165],{"class":61},[55,450,451],{"class":82}," csv.",[55,453,454],{"class":65},"NewReader",[55,456,457],{"class":82},"(f)\n",[55,459,461,464,466,469,472],{"class":57,"line":460},30,[55,462,463],{"class":82},"    records, err ",[55,465,165],{"class":61},[55,467,468],{"class":82}," r.",[55,470,471],{"class":65},"ReadAll",[55,473,260],{"class":82},[55,475,477,479,481,483,485],{"class":57,"line":476},31,[55,478,271],{"class":61},[55,480,372],{"class":82},[55,482,375],{"class":61},[55,484,378],{"class":212},[55,486,298],{"class":82},[55,488,490,492,494,496,499,501,503],{"class":57,"line":489},32,[55,491,304],{"class":82},[55,493,388],{"class":65},[55,495,310],{"class":82},[55,497,498],{"class":89},"\"read csv: ",[55,500,396],{"class":212},[55,502,399],{"class":89},[55,504,402],{"class":82},[55,506,508,510,512,514,516],{"class":57,"line":507},33,[55,509,321],{"class":82},[55,511,324],{"class":65},[55,513,174],{"class":82},[55,515,414],{"class":212},[55,517,139],{"class":82},[55,519,521],{"class":57,"line":520},34,[55,522,337],{"class":82},[55,524,526],{"class":57,"line":525},35,[55,527,73],{"emptyLinePlaceholder":72},[55,529,531,534,537,540,543,546,549],{"class":57,"line":530},36,[55,532,533],{"class":61},"    var",[55,535,536],{"class":82}," result []",[55,538,539],{"class":61},"map",[55,541,542],{"class":82},"[",[55,544,545],{"class":61},"string",[55,547,548],{"class":82},"]",[55,550,551],{"class":61},"string\n",[55,553,555,557,560,563,565,568],{"class":57,"line":554},37,[55,556,271],{"class":61},[55,558,559],{"class":65}," len",[55,561,562],{"class":82},"(records) ",[55,564,280],{"class":61},[55,566,567],{"class":212}," 0",[55,569,298],{"class":82},[55,571,573],{"class":57,"line":572},38,[55,574,575],{"class":61},"        return\n",[55,577,579],{"class":57,"line":578},39,[55,580,337],{"class":82},[55,582,584,587,589,592,594],{"class":57,"line":583},40,[55,585,586],{"class":82},"    headers ",[55,588,165],{"class":61},[55,590,591],{"class":82}," records[",[55,593,213],{"class":212},[55,595,596],{"class":82},"]\n",[55,598,600,603,606,608,611,613,615],{"class":57,"line":599},41,[55,601,602],{"class":61},"    for",[55,604,605],{"class":82}," _, row ",[55,607,165],{"class":61},[55,609,610],{"class":61}," range",[55,612,591],{"class":82},[55,614,414],{"class":212},[55,616,617],{"class":82},":] {\n",[55,619,621,624,626,629,632,634,637,640,643,645,648,650,652],{"class":57,"line":620},42,[55,622,623],{"class":61},"        if",[55,625,559],{"class":65},[55,627,628],{"class":82},"(row) ",[55,630,631],{"class":61},">",[55,633,274],{"class":61},[55,635,636],{"class":82},"filterCol ",[55,638,639],{"class":61},"&&",[55,641,642],{"class":82}," row[",[55,644,361],{"class":61},[55,646,647],{"class":82},"filterCol] ",[55,649,280],{"class":61},[55,651,274],{"class":61},[55,653,654],{"class":82},"filterVal {\n",[55,656,658,661,663,666,668,670,672,674,676,678],{"class":57,"line":657},43,[55,659,660],{"class":82},"            m ",[55,662,165],{"class":61},[55,664,665],{"class":65}," make",[55,667,174],{"class":82},[55,669,539],{"class":61},[55,671,542],{"class":82},[55,673,545],{"class":61},[55,675,548],{"class":82},[55,677,545],{"class":61},[55,679,139],{"class":82},[55,681,683,686,689,691,693],{"class":57,"line":682},44,[55,684,685],{"class":61},"            for",[55,687,688],{"class":82}," i, h ",[55,690,165],{"class":61},[55,692,610],{"class":61},[55,694,695],{"class":82}," headers {\n",[55,697,699,702,705,708,710],{"class":57,"line":698},45,[55,700,701],{"class":61},"                if",[55,703,704],{"class":82}," i ",[55,706,707],{"class":61},"\u003C",[55,709,559],{"class":65},[55,711,712],{"class":82},"(row) {\n",[55,714,716,719,722],{"class":57,"line":715},46,[55,717,718],{"class":82},"                    m[h] ",[55,720,721],{"class":61},"=",[55,723,724],{"class":82}," row[i]\n",[55,726,728],{"class":57,"line":727},47,[55,729,730],{"class":82},"                }\n",[55,732,734],{"class":57,"line":733},48,[55,735,736],{"class":82},"            }\n",[55,738,740,743,745,748],{"class":57,"line":739},49,[55,741,742],{"class":82},"            result ",[55,744,721],{"class":61},[55,746,747],{"class":65}," append",[55,749,750],{"class":82},"(result, m)\n",[55,752,754],{"class":57,"line":753},50,[55,755,756],{"class":82},"        }\n",[55,758,760],{"class":57,"line":759},51,[55,761,337],{"class":82},[55,763,765],{"class":57,"line":764},52,[55,766,73],{"emptyLinePlaceholder":72},[55,768,770,773,775,778,781],{"class":57,"line":769},53,[55,771,772],{"class":82},"    enc ",[55,774,165],{"class":61},[55,776,777],{"class":82}," json.",[55,779,780],{"class":65},"NewEncoder",[55,782,783],{"class":82},"(os.Stdout)\n",[55,785,787,790,793,795,797,799,802],{"class":57,"line":786},54,[55,788,789],{"class":82},"    enc.",[55,791,792],{"class":65},"SetIndent",[55,794,174],{"class":82},[55,796,182],{"class":89},[55,798,34],{"class":82},[55,800,801],{"class":89},"\"  \"",[55,803,139],{"class":82},[55,805,807,809,812],{"class":57,"line":806},55,[55,808,789],{"class":82},[55,810,811],{"class":65},"Encode",[55,813,814],{"class":82},"(result)\n",[55,816,818],{"class":57,"line":817},56,[55,819,820],{"class":82},"}\n",[23,822,824],{"id":823},"_2-stack-and-queue-with-generics","2. Stack and Queue with Generics",[14,826,827,828,831,832,835,836,34,839,34,842,845],{},"Implement a generic ",[31,829,830],{},"Stack[T]"," and ",[31,833,834],{},"Queue[T]"," using slices. Include ",[31,837,838],{},"Push",[31,840,841],{},"Pop",[31,843,844],{},"Len",", and tests.",[43,847,848,1169,1173,1185,1189,1202,1206,1210,1217,1221,1224,1231,1256,1260,1271,1275,1290],{"language":45},[47,849,851],{"className":49,"code":850,"language":45,"meta":51,"style":51},"package stack\n\ntype Stack[T any] struct{ items []T }\n\nfunc (s *Stack[T]) Push(v T) {\n    s.items = append(s.items, v)\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n    var zero T\n    if len(s.items) == 0 {\n        return zero, false\n    }\n    v := s.items[len(s.items)-1]\n    s.items = s.items[:len(s.items)-1]\n    return v, true\n}\n\nfunc (s *Stack[T]) Len() int { return len(s.items) }\n\n\u002F\u002F Tests:\n\u002F\u002F func TestStack(t *testing.T) {\n\u002F\u002F     s := &Stack[int]{}\n\u002F\u002F     s.Push(1); s.Push(2)\n\u002F\u002F     v, ok := s.Pop()\n\u002F\u002F     assert(v == 2 && ok)\n\u002F\u002F     assert(s.Len() == 1)\n\u002F\u002F }\n",[31,852,853,860,864,895,899,934,946,950,954,986,996,1011,1022,1026,1049,1068,1079,1083,1087,1124,1128,1134,1139,1144,1149,1154,1159,1164],{"__ignoreMap":51},[55,854,855,857],{"class":57,"line":58},[55,856,62],{"class":61},[55,858,859],{"class":65}," stack\n",[55,861,862],{"class":57,"line":69},[55,863,73],{"emptyLinePlaceholder":72},[55,865,866,869,872,874,878,881,884,887,890,892],{"class":57,"line":76},[55,867,868],{"class":61},"type",[55,870,871],{"class":65}," Stack",[55,873,542],{"class":82},[55,875,877],{"class":876},"sCrzJ","T",[55,879,880],{"class":65}," any",[55,882,883],{"class":82},"] ",[55,885,886],{"class":61},"struct",[55,888,889],{"class":82},"{ items []",[55,891,877],{"class":65},[55,893,894],{"class":82}," }\n",[55,896,897],{"class":57,"line":86},[55,898,73],{"emptyLinePlaceholder":72},[55,900,901,903,906,909,911,914,916,918,921,923,925,928,931],{"class":57,"line":98},[55,902,150],{"class":61},[55,904,905],{"class":82}," (",[55,907,908],{"class":876},"s ",[55,910,361],{"class":61},[55,912,913],{"class":65},"Stack",[55,915,542],{"class":82},[55,917,877],{"class":65},[55,919,920],{"class":82},"]) ",[55,922,838],{"class":65},[55,924,174],{"class":82},[55,926,927],{"class":876},"v",[55,929,930],{"class":65}," T",[55,932,933],{"class":82},") {\n",[55,935,936,939,941,943],{"class":57,"line":107},[55,937,938],{"class":82},"    s.items ",[55,940,721],{"class":61},[55,942,747],{"class":65},[55,944,945],{"class":82},"(s.items, v)\n",[55,947,948],{"class":57,"line":116},[55,949,820],{"class":82},[55,951,952],{"class":57,"line":126},[55,953,73],{"emptyLinePlaceholder":72},[55,955,956,958,960,962,964,966,968,970,972,974,977,979,981,984],{"class":57,"line":136},[55,957,150],{"class":61},[55,959,905],{"class":82},[55,961,908],{"class":876},[55,963,361],{"class":61},[55,965,913],{"class":65},[55,967,542],{"class":82},[55,969,877],{"class":65},[55,971,920],{"class":82},[55,973,841],{"class":65},[55,975,976],{"class":82},"() (",[55,978,877],{"class":65},[55,980,34],{"class":82},[55,982,983],{"class":61},"bool",[55,985,933],{"class":82},[55,987,988,990,993],{"class":57,"line":142},[55,989,533],{"class":61},[55,991,992],{"class":82}," zero ",[55,994,995],{"class":65},"T\n",[55,997,998,1000,1002,1005,1007,1009],{"class":57,"line":147},[55,999,271],{"class":61},[55,1001,559],{"class":65},[55,1003,1004],{"class":82},"(s.items) ",[55,1006,280],{"class":61},[55,1008,567],{"class":212},[55,1010,298],{"class":82},[55,1012,1013,1016,1019],{"class":57,"line":159},[55,1014,1015],{"class":61},"        return",[55,1017,1018],{"class":82}," zero, ",[55,1020,1021],{"class":212},"false\n",[55,1023,1024],{"class":57,"line":192},[55,1025,337],{"class":82},[55,1027,1028,1031,1033,1036,1039,1042,1045,1047],{"class":57,"line":223},[55,1029,1030],{"class":82},"    v ",[55,1032,165],{"class":61},[55,1034,1035],{"class":82}," s.items[",[55,1037,1038],{"class":65},"len",[55,1040,1041],{"class":82},"(s.items)",[55,1043,1044],{"class":61},"-",[55,1046,414],{"class":212},[55,1048,596],{"class":82},[55,1050,1051,1053,1055,1058,1060,1062,1064,1066],{"class":57,"line":251},[55,1052,938],{"class":82},[55,1054,721],{"class":61},[55,1056,1057],{"class":82}," s.items[:",[55,1059,1038],{"class":65},[55,1061,1041],{"class":82},[55,1063,1044],{"class":61},[55,1065,414],{"class":212},[55,1067,596],{"class":82},[55,1069,1070,1073,1076],{"class":57,"line":263},[55,1071,1072],{"class":61},"    return",[55,1074,1075],{"class":82}," v, ",[55,1077,1078],{"class":212},"true\n",[55,1080,1081],{"class":57,"line":268},[55,1082,820],{"class":82},[55,1084,1085],{"class":57,"line":301},[55,1086,73],{"emptyLinePlaceholder":72},[55,1088,1089,1091,1093,1095,1097,1099,1101,1103,1105,1107,1110,1113,1116,1119,1121],{"class":57,"line":318},[55,1090,150],{"class":61},[55,1092,905],{"class":82},[55,1094,908],{"class":876},[55,1096,361],{"class":61},[55,1098,913],{"class":65},[55,1100,542],{"class":82},[55,1102,877],{"class":65},[55,1104,920],{"class":82},[55,1106,844],{"class":65},[55,1108,1109],{"class":82},"() ",[55,1111,1112],{"class":61},"int",[55,1114,1115],{"class":82}," { ",[55,1117,1118],{"class":61},"return",[55,1120,559],{"class":65},[55,1122,1123],{"class":82},"(s.items) }\n",[55,1125,1126],{"class":57,"line":334},[55,1127,73],{"emptyLinePlaceholder":72},[55,1129,1130],{"class":57,"line":340},[55,1131,1133],{"class":1132},"sdCPZ","\u002F\u002F Tests:\n",[55,1135,1136],{"class":57,"line":345},[55,1137,1138],{"class":1132},"\u002F\u002F func TestStack(t *testing.T) {\n",[55,1140,1141],{"class":57,"line":367},[55,1142,1143],{"class":1132},"\u002F\u002F     s := &Stack[int]{}\n",[55,1145,1146],{"class":57,"line":383},[55,1147,1148],{"class":1132},"\u002F\u002F     s.Push(1); s.Push(2)\n",[55,1150,1151],{"class":57,"line":405},[55,1152,1153],{"class":1132},"\u002F\u002F     v, ok := s.Pop()\n",[55,1155,1156],{"class":57,"line":419},[55,1157,1158],{"class":1132},"\u002F\u002F     assert(v == 2 && ok)\n",[55,1160,1161],{"class":57,"line":424},[55,1162,1163],{"class":1132},"\u002F\u002F     assert(s.Len() == 1)\n",[55,1165,1166],{"class":57,"line":438},[55,1167,1168],{"class":1132},"\u002F\u002F }\n",[23,1170,1172],{"id":1171},"_3-word-frequency-counter","3. Word Frequency Counter",[14,1174,1175,1176,34,1179,34,1182,1184],{},"Read a text file, count word frequencies, print the top 10. Use ",[31,1177,1178],{},"bufio.Scanner",[31,1180,1181],{},"strings.Fields",[31,1183,539],{},", sorting.",[23,1186,1188],{"id":1187},"_4-safe-url-shortener","4. Safe URL Shortener",[14,1190,1191,1192,1195,1196,831,1199,41],{},"Implement an in-memory URL shortener with a ",[31,1193,1194],{},"sync.RWMutex","-protected map. Support ",[31,1197,1198],{},"Shorten(url) string",[31,1200,1201],{},"Resolve(short) (string, error)",[18,1203,1205],{"id":1204},"intermediate","Intermediate",[23,1207,1209],{"id":1208},"_5-worker-pool-with-cancellation","5. Worker Pool with Cancellation",[14,1211,1212,1213,1216],{},"Implement a worker pool that processes jobs from a channel, respects ",[31,1214,1215],{},"context.Context",", and returns results. Test with 1000 jobs and 10 workers, ensuring all results are collected even on cancellation.",[23,1218,1220],{"id":1219},"_6-pipeline-with-backpressure","6. Pipeline with Backpressure",[14,1222,1223],{},"Build a 3-stage pipeline (generate → process → collect) with bounded buffers. Measure what happens with unbuffered vs buffered channels. Write benchmarks for each.",[23,1225,1227,1228],{"id":1226},"_7-custom-error-types-with-errorsas","7. Custom Error Types with ",[31,1229,1230],{},"errors.As",[14,1232,1233,1234,1237,1238,34,1241,1244,1245,1248,1249,1252,1253,1255],{},"Define a ",[31,1235,1236],{},"ValidationError"," with ",[31,1239,1240],{},"Field",[31,1242,1243],{},"Message",", and ",[31,1246,1247],{},"Code",". Implement ",[31,1250,1251],{},"Error()",". Write a function that returns wrapped errors, and a caller that uses ",[31,1254,1230],{}," to extract the validation details for HTTP status mapping.",[23,1257,1259],{"id":1258},"_8-rate-limiter-token-bucket","8. Rate Limiter (Token Bucket)",[14,1261,1262,1263,1266,1267,1270],{},"Implement a token-bucket rate limiter using a ",[31,1264,1265],{},"time.Ticker"," and a channel. Support ",[31,1268,1269],{},"Allow() bool"," for rate-limited access. Test with concurrent goroutines.",[23,1272,1274],{"id":1273},"_9-concurrent-cache-with-ttl","9. Concurrent Cache with TTL",[14,1276,1277,1278,1280,1281,34,1284,1244,1287,41],{},"Build a cache with time-based expiration. Use ",[31,1279,1194],{},", a map, and a background goroutine for eviction. Support ",[31,1282,1283],{},"Get",[31,1285,1286],{},"Set(key, val, ttl)",[31,1288,1289],{},"Delete",[43,1291,1292,1991,1995,1999,2002,2034],{"language":45},[47,1293,1295],{"className":49,"code":1294,"language":45,"meta":51,"style":51},"package cache\n\nimport (\n    \"sync\"\n    \"time\"\n)\n\ntype entry struct {\n    value any\n    expiry time.Time\n}\n\ntype Cache struct {\n    mu      sync.RWMutex\n    items   map[string]entry\n}\n\nfunc New() *Cache {\n    return &Cache{items: make(map[string]entry)}\n}\n\nfunc (c *Cache) Set(key string, val any, ttl time.Duration) {\n    c.mu.Lock()\n    defer c.mu.Unlock()\n    c.items[key] = entry{value: val, expiry: time.Now().Add(ttl)}\n}\n\nfunc (c *Cache) Get(key string) (any, bool) {\n    c.mu.RLock()\n    e, ok := c.items[key]\n    c.mu.RUnlock()\n    if !ok {\n        return nil, false\n    }\n    if time.Now().After(e.expiry) {\n        c.mu.Lock()\n        delete(c.items, key)  \u002F\u002F lazy eviction\n        c.mu.Unlock()\n        return nil, false\n    }\n    return e.value, true\n}\n\nfunc (c *Cache) StartEvictor(ctx context.Context, interval time.Duration) {\n    ticker := time.NewTicker(interval)\n    defer ticker.Stop()\n    for {\n        select {\n        case \u003C-ticker.C:\n            c.evictExpired()\n        case \u003C-ctx.Done():\n            return\n        }\n    }\n}\n\nfunc (c *Cache) evictExpired() {\n    now := time.Now()\n    c.mu.Lock()\n    defer c.mu.Unlock()\n    for k, e := range c.items {\n        if now.After(e.expiry) {\n            delete(c.items, k)\n        }\n    }\n}\n",[31,1296,1297,1304,1308,1314,1323,1332,1336,1340,1352,1360,1372,1376,1380,1391,1403,1419,1423,1427,1443,1474,1478,1482,1531,1541,1553,1577,1581,1585,1619,1628,1638,1647,1657,1667,1671,1688,1697,1708,1716,1726,1730,1739,1743,1747,1790,1805,1817,1823,1830,1841,1851,1866,1871,1875,1879,1883,1887,1906,1920,1929,1940,1955,1967,1976,1981,1986],{"__ignoreMap":51},[55,1298,1299,1301],{"class":57,"line":58},[55,1300,62],{"class":61},[55,1302,1303],{"class":65}," cache\n",[55,1305,1306],{"class":57,"line":69},[55,1307,73],{"emptyLinePlaceholder":72},[55,1309,1310,1312],{"class":57,"line":76},[55,1311,79],{"class":61},[55,1313,83],{"class":82},[55,1315,1316,1318,1321],{"class":57,"line":86},[55,1317,90],{"class":89},[55,1319,1320],{"class":65},"sync",[55,1322,95],{"class":89},[55,1324,1325,1327,1330],{"class":57,"line":98},[55,1326,90],{"class":89},[55,1328,1329],{"class":65},"time",[55,1331,95],{"class":89},[55,1333,1334],{"class":57,"line":107},[55,1335,139],{"class":82},[55,1337,1338],{"class":57,"line":116},[55,1339,73],{"emptyLinePlaceholder":72},[55,1341,1342,1344,1347,1350],{"class":57,"line":126},[55,1343,868],{"class":61},[55,1345,1346],{"class":65}," entry",[55,1348,1349],{"class":61}," struct",[55,1351,298],{"class":82},[55,1353,1354,1357],{"class":57,"line":136},[55,1355,1356],{"class":82},"    value ",[55,1358,1359],{"class":65},"any\n",[55,1361,1362,1365,1367,1369],{"class":57,"line":142},[55,1363,1364],{"class":82},"    expiry ",[55,1366,1329],{"class":65},[55,1368,41],{"class":82},[55,1370,1371],{"class":65},"Time\n",[55,1373,1374],{"class":57,"line":147},[55,1375,820],{"class":82},[55,1377,1378],{"class":57,"line":159},[55,1379,73],{"emptyLinePlaceholder":72},[55,1381,1382,1384,1387,1389],{"class":57,"line":192},[55,1383,868],{"class":61},[55,1385,1386],{"class":65}," Cache",[55,1388,1349],{"class":61},[55,1390,298],{"class":82},[55,1392,1393,1396,1398,1400],{"class":57,"line":223},[55,1394,1395],{"class":82},"    mu      ",[55,1397,1320],{"class":65},[55,1399,41],{"class":82},[55,1401,1402],{"class":65},"RWMutex\n",[55,1404,1405,1408,1410,1412,1414,1416],{"class":57,"line":251},[55,1406,1407],{"class":82},"    items   ",[55,1409,539],{"class":61},[55,1411,542],{"class":82},[55,1413,545],{"class":61},[55,1415,548],{"class":82},[55,1417,1418],{"class":65},"entry\n",[55,1420,1421],{"class":57,"line":263},[55,1422,820],{"class":82},[55,1424,1425],{"class":57,"line":268},[55,1426,73],{"emptyLinePlaceholder":72},[55,1428,1429,1431,1434,1436,1438,1441],{"class":57,"line":301},[55,1430,150],{"class":61},[55,1432,1433],{"class":65}," New",[55,1435,1109],{"class":82},[55,1437,361],{"class":61},[55,1439,1440],{"class":65},"Cache",[55,1442,298],{"class":82},[55,1444,1445,1447,1450,1452,1455,1458,1460,1462,1464,1466,1468,1471],{"class":57,"line":318},[55,1446,1072],{"class":61},[55,1448,1449],{"class":61}," &",[55,1451,1440],{"class":65},[55,1453,1454],{"class":82},"{items: ",[55,1456,1457],{"class":65},"make",[55,1459,174],{"class":82},[55,1461,539],{"class":61},[55,1463,542],{"class":82},[55,1465,545],{"class":61},[55,1467,548],{"class":82},[55,1469,1470],{"class":65},"entry",[55,1472,1473],{"class":82},")}\n",[55,1475,1476],{"class":57,"line":334},[55,1477,820],{"class":82},[55,1479,1480],{"class":57,"line":340},[55,1481,73],{"emptyLinePlaceholder":72},[55,1483,1484,1486,1488,1491,1493,1495,1498,1501,1503,1506,1509,1511,1514,1516,1518,1521,1524,1526,1529],{"class":57,"line":345},[55,1485,150],{"class":61},[55,1487,905],{"class":82},[55,1489,1490],{"class":876},"c ",[55,1492,361],{"class":61},[55,1494,1440],{"class":65},[55,1496,1497],{"class":82},") ",[55,1499,1500],{"class":65},"Set",[55,1502,174],{"class":82},[55,1504,1505],{"class":876},"key",[55,1507,1508],{"class":61}," string",[55,1510,34],{"class":82},[55,1512,1513],{"class":876},"val",[55,1515,880],{"class":65},[55,1517,34],{"class":82},[55,1519,1520],{"class":876},"ttl",[55,1522,1523],{"class":65}," time",[55,1525,41],{"class":82},[55,1527,1528],{"class":65},"Duration",[55,1530,933],{"class":82},[55,1532,1533,1536,1539],{"class":57,"line":367},[55,1534,1535],{"class":82},"    c.mu.",[55,1537,1538],{"class":65},"Lock",[55,1540,260],{"class":82},[55,1542,1543,1545,1548,1551],{"class":57,"line":383},[55,1544,427],{"class":61},[55,1546,1547],{"class":82}," c.mu.",[55,1549,1550],{"class":65},"Unlock",[55,1552,260],{"class":82},[55,1554,1555,1558,1560,1562,1565,1568,1571,1574],{"class":57,"line":405},[55,1556,1557],{"class":82},"    c.items[key] ",[55,1559,721],{"class":61},[55,1561,1346],{"class":65},[55,1563,1564],{"class":82},"{value: val, expiry: time.",[55,1566,1567],{"class":65},"Now",[55,1569,1570],{"class":82},"().",[55,1572,1573],{"class":65},"Add",[55,1575,1576],{"class":82},"(ttl)}\n",[55,1578,1579],{"class":57,"line":419},[55,1580,820],{"class":82},[55,1582,1583],{"class":57,"line":424},[55,1584,73],{"emptyLinePlaceholder":72},[55,1586,1587,1589,1591,1593,1595,1597,1599,1601,1603,1605,1607,1610,1613,1615,1617],{"class":57,"line":438},[55,1588,150],{"class":61},[55,1590,905],{"class":82},[55,1592,1490],{"class":876},[55,1594,361],{"class":61},[55,1596,1440],{"class":65},[55,1598,1497],{"class":82},[55,1600,1283],{"class":65},[55,1602,174],{"class":82},[55,1604,1505],{"class":876},[55,1606,1508],{"class":61},[55,1608,1609],{"class":82},") (",[55,1611,1612],{"class":65},"any",[55,1614,34],{"class":82},[55,1616,983],{"class":61},[55,1618,933],{"class":82},[55,1620,1621,1623,1626],{"class":57,"line":443},[55,1622,1535],{"class":82},[55,1624,1625],{"class":65},"RLock",[55,1627,260],{"class":82},[55,1629,1630,1633,1635],{"class":57,"line":460},[55,1631,1632],{"class":82},"    e, ok ",[55,1634,165],{"class":61},[55,1636,1637],{"class":82}," c.items[key]\n",[55,1639,1640,1642,1645],{"class":57,"line":476},[55,1641,1535],{"class":82},[55,1643,1644],{"class":65},"RUnlock",[55,1646,260],{"class":82},[55,1648,1649,1651,1654],{"class":57,"line":489},[55,1650,271],{"class":61},[55,1652,1653],{"class":61}," !",[55,1655,1656],{"class":82},"ok {\n",[55,1658,1659,1661,1663,1665],{"class":57,"line":507},[55,1660,1015],{"class":61},[55,1662,378],{"class":212},[55,1664,34],{"class":82},[55,1666,1021],{"class":212},[55,1668,1669],{"class":57,"line":520},[55,1670,337],{"class":82},[55,1672,1673,1675,1678,1680,1682,1685],{"class":57,"line":525},[55,1674,271],{"class":61},[55,1676,1677],{"class":82}," time.",[55,1679,1567],{"class":65},[55,1681,1570],{"class":82},[55,1683,1684],{"class":65},"After",[55,1686,1687],{"class":82},"(e.expiry) {\n",[55,1689,1690,1693,1695],{"class":57,"line":530},[55,1691,1692],{"class":82},"        c.mu.",[55,1694,1538],{"class":65},[55,1696,260],{"class":82},[55,1698,1699,1702,1705],{"class":57,"line":554},[55,1700,1701],{"class":65},"        delete",[55,1703,1704],{"class":82},"(c.items, key)  ",[55,1706,1707],{"class":1132},"\u002F\u002F lazy eviction\n",[55,1709,1710,1712,1714],{"class":57,"line":572},[55,1711,1692],{"class":82},[55,1713,1550],{"class":65},[55,1715,260],{"class":82},[55,1717,1718,1720,1722,1724],{"class":57,"line":578},[55,1719,1015],{"class":61},[55,1721,378],{"class":212},[55,1723,34],{"class":82},[55,1725,1021],{"class":212},[55,1727,1728],{"class":57,"line":583},[55,1729,337],{"class":82},[55,1731,1732,1734,1737],{"class":57,"line":599},[55,1733,1072],{"class":61},[55,1735,1736],{"class":82}," e.value, ",[55,1738,1078],{"class":212},[55,1740,1741],{"class":57,"line":620},[55,1742,820],{"class":82},[55,1744,1745],{"class":57,"line":657},[55,1746,73],{"emptyLinePlaceholder":72},[55,1748,1749,1751,1753,1755,1757,1759,1761,1764,1766,1769,1772,1774,1777,1779,1782,1784,1786,1788],{"class":57,"line":682},[55,1750,150],{"class":61},[55,1752,905],{"class":82},[55,1754,1490],{"class":876},[55,1756,361],{"class":61},[55,1758,1440],{"class":65},[55,1760,1497],{"class":82},[55,1762,1763],{"class":65},"StartEvictor",[55,1765,174],{"class":82},[55,1767,1768],{"class":876},"ctx",[55,1770,1771],{"class":65}," context",[55,1773,41],{"class":82},[55,1775,1776],{"class":65},"Context",[55,1778,34],{"class":82},[55,1780,1781],{"class":876},"interval",[55,1783,1523],{"class":65},[55,1785,41],{"class":82},[55,1787,1528],{"class":65},[55,1789,933],{"class":82},[55,1791,1792,1795,1797,1799,1802],{"class":57,"line":698},[55,1793,1794],{"class":82},"    ticker ",[55,1796,165],{"class":61},[55,1798,1677],{"class":82},[55,1800,1801],{"class":65},"NewTicker",[55,1803,1804],{"class":82},"(interval)\n",[55,1806,1807,1809,1812,1815],{"class":57,"line":715},[55,1808,427],{"class":61},[55,1810,1811],{"class":82}," ticker.",[55,1813,1814],{"class":65},"Stop",[55,1816,260],{"class":82},[55,1818,1819,1821],{"class":57,"line":727},[55,1820,602],{"class":61},[55,1822,298],{"class":82},[55,1824,1825,1828],{"class":57,"line":733},[55,1826,1827],{"class":61},"        select",[55,1829,298],{"class":82},[55,1831,1832,1835,1838],{"class":57,"line":739},[55,1833,1834],{"class":61},"        case",[55,1836,1837],{"class":61}," \u003C-",[55,1839,1840],{"class":82},"ticker.C:\n",[55,1842,1843,1846,1849],{"class":57,"line":753},[55,1844,1845],{"class":82},"            c.",[55,1847,1848],{"class":65},"evictExpired",[55,1850,260],{"class":82},[55,1852,1853,1855,1857,1860,1863],{"class":57,"line":759},[55,1854,1834],{"class":61},[55,1856,1837],{"class":61},[55,1858,1859],{"class":82},"ctx.",[55,1861,1862],{"class":65},"Done",[55,1864,1865],{"class":82},"():\n",[55,1867,1868],{"class":57,"line":764},[55,1869,1870],{"class":61},"            return\n",[55,1872,1873],{"class":57,"line":769},[55,1874,756],{"class":82},[55,1876,1877],{"class":57,"line":786},[55,1878,337],{"class":82},[55,1880,1881],{"class":57,"line":806},[55,1882,820],{"class":82},[55,1884,1885],{"class":57,"line":817},[55,1886,73],{"emptyLinePlaceholder":72},[55,1888,1890,1892,1894,1896,1898,1900,1902,1904],{"class":57,"line":1889},57,[55,1891,150],{"class":61},[55,1893,905],{"class":82},[55,1895,1490],{"class":876},[55,1897,361],{"class":61},[55,1899,1440],{"class":65},[55,1901,1497],{"class":82},[55,1903,1848],{"class":65},[55,1905,156],{"class":82},[55,1907,1909,1912,1914,1916,1918],{"class":57,"line":1908},58,[55,1910,1911],{"class":82},"    now ",[55,1913,165],{"class":61},[55,1915,1677],{"class":82},[55,1917,1567],{"class":65},[55,1919,260],{"class":82},[55,1921,1923,1925,1927],{"class":57,"line":1922},59,[55,1924,1535],{"class":82},[55,1926,1538],{"class":65},[55,1928,260],{"class":82},[55,1930,1932,1934,1936,1938],{"class":57,"line":1931},60,[55,1933,427],{"class":61},[55,1935,1547],{"class":82},[55,1937,1550],{"class":65},[55,1939,260],{"class":82},[55,1941,1943,1945,1948,1950,1952],{"class":57,"line":1942},61,[55,1944,602],{"class":61},[55,1946,1947],{"class":82}," k, e ",[55,1949,165],{"class":61},[55,1951,610],{"class":61},[55,1953,1954],{"class":82}," c.items {\n",[55,1956,1958,1960,1963,1965],{"class":57,"line":1957},62,[55,1959,623],{"class":61},[55,1961,1962],{"class":82}," now.",[55,1964,1684],{"class":65},[55,1966,1687],{"class":82},[55,1968,1970,1973],{"class":57,"line":1969},63,[55,1971,1972],{"class":65},"            delete",[55,1974,1975],{"class":82},"(c.items, k)\n",[55,1977,1979],{"class":57,"line":1978},64,[55,1980,756],{"class":82},[55,1982,1984],{"class":57,"line":1983},65,[55,1985,337],{"class":82},[55,1987,1989],{"class":57,"line":1988},66,[55,1990,820],{"class":82},[18,1992,1994],{"id":1993},"advanced","Advanced",[23,1996,1998],{"id":1997},"_10-concurrent-web-crawler","10. Concurrent Web Crawler",[14,2000,2001],{},"Build a web crawler that:",[2003,2004,2005,2009,2012,2019,2025,2031],"ul",{},[2006,2007,2008],"li",{},"Starts from a seed URL, fetches pages concurrently (worker pool)",[2006,2010,2011],{},"Extracts links, follows them (bounded depth)",[2006,2013,2014,2015,2018],{},"Respects ",[31,2016,2017],{},"robots.txt"," and rate limits per domain",[2006,2020,2021,2022,2024],{},"Uses ",[31,2023,1215],{}," for cancellation and timeouts",[2006,2026,2027,2028],{},"Deduplicates visited URLs with a ",[31,2029,2030],{},"sync.Map",[2006,2032,2033],{},"Reports results to a channel",[43,2035,2036,3242,3246,3256,3260,3274,3278,3287],{"language":45},[47,2037,2039],{"className":49,"code":2038,"language":45,"meta":51,"style":51},"package crawler\n\nimport (\n    \"context\"\n    \"net\u002Fhttp\"\n    \"net\u002Furl\"\n    \"sync\"\n    \"time\"\n)\n\ntype Crawler struct {\n    client    *http.Client\n    workers   int\n    maxDepth  int\n    visited   sync.Map  \u002F\u002F URL → struct{}\n    rateLimit time.Duration\n}\n\ntype Result struct {\n    URL   string\n    Title string\n    Depth int\n    Err   error\n}\n\nfunc New(workers, maxDepth int, rateLimit time.Duration) *Crawler {\n    return &Crawler{\n        client:    &http.Client{Timeout: 10 * time.Second},\n        workers:   workers,\n        maxDepth:  maxDepth,\n        rateLimit: rateLimit,\n    }\n}\n\nfunc (c *Crawler) Crawl(ctx context.Context, seed string) \u003C-chan Result {\n    out := make(chan Result, 100)\n    jobs := make(chan job, 100)\n\n    var wg sync.WaitGroup\n    for i := 0; i \u003C c.workers; i++ {\n        wg.Add(1)\n        go func() {\n            defer wg.Done()\n            c.worker(ctx, jobs, out)\n        }()\n    }\n\n    go func() {\n        defer close(jobs)\n        c.enqueue(ctx, jobs, seed, 0)\n    }()\n\n    go func() {\n        wg.Wait()\n        close(out)\n    }()\n\n    return out\n}\n\ntype job struct {\n    url   string\n    depth int\n}\n\nfunc (c *Crawler) enqueue(ctx context.Context, jobs chan\u003C- job, u string, depth int) {\n    if depth > c.maxDepth {\n        return\n    }\n    if _, loaded := c.visited.LoadOrStore(u, struct{}{}); loaded {\n        return  \u002F\u002F already visited\n    }\n    select {\n    case jobs \u003C- job{url: u, depth: depth}:\n    case \u003C-ctx.Done():\n    }\n}\n\nfunc (c *Crawler) worker(ctx context.Context, jobs \u003C-chan job, out chan\u003C- Result) {\n    for {\n        select {\n        case j, ok := \u003C-jobs:\n            if !ok {\n                return\n            }\n            result, links := c.fetch(ctx, j)\n            select {\n            case out \u003C- result:\n            case \u003C-ctx.Done():\n                return\n            }\n            for _, link := range links {\n                c.enqueue(ctx, jobs, link, j.depth+1)\n            }\n        case \u003C-ctx.Done():\n            return\n        }\n    }\n}\n\nfunc (c *Crawler) fetch(ctx context.Context, j job) (Result, []string) {\n    req, err := http.NewRequestWithContext(ctx, \"GET\", j.url, nil)\n    if err != nil {\n        return Result{URL: j.url, Depth: j.depth, Err: err}, nil\n    }\n    resp, err := c.client.Do(req)\n    if err != nil {\n        return Result{URL: j.url, Depth: j.depth, Err: err}, nil\n    }\n    defer resp.Body.Close()\n\n    title, links := parseHTML(resp.Body, j.url)\n    return Result{URL: j.url, Title: title, Depth: j.depth}, links\n}\n",[31,2040,2041,2048,2052,2058,2067,2076,2085,2093,2101,2105,2109,2120,2135,2143,2150,2165,2177,2181,2185,2196,2203,2210,2217,2225,2229,2233,2272,2283,2309,2314,2319,2324,2328,2332,2336,2379,2402,2424,2428,2442,2465,2478,2488,2500,2510,2515,2519,2523,2532,2543,2558,2563,2567,2575,2584,2592,2596,2600,2607,2611,2615,2625,2632,2639,2643,2647,2699,2712,2717,2722,2746,2754,2759,2767,2782,2795,2800,2805,2810,2857,2864,2871,2886,2896,2902,2907,2924,2932,2946,2959,2964,2969,2984,3002,3007,3020,3025,3030,3035,3040,3045,3091,3119,3132,3145,3150,3167,3180,3191,3196,3208,3213,3227,3237],{"__ignoreMap":51},[55,2042,2043,2045],{"class":57,"line":58},[55,2044,62],{"class":61},[55,2046,2047],{"class":65}," crawler\n",[55,2049,2050],{"class":57,"line":69},[55,2051,73],{"emptyLinePlaceholder":72},[55,2053,2054,2056],{"class":57,"line":76},[55,2055,79],{"class":61},[55,2057,83],{"class":82},[55,2059,2060,2062,2065],{"class":57,"line":86},[55,2061,90],{"class":89},[55,2063,2064],{"class":65},"context",[55,2066,95],{"class":89},[55,2068,2069,2071,2074],{"class":57,"line":98},[55,2070,90],{"class":89},[55,2072,2073],{"class":65},"net\u002Fhttp",[55,2075,95],{"class":89},[55,2077,2078,2080,2083],{"class":57,"line":107},[55,2079,90],{"class":89},[55,2081,2082],{"class":65},"net\u002Furl",[55,2084,95],{"class":89},[55,2086,2087,2089,2091],{"class":57,"line":116},[55,2088,90],{"class":89},[55,2090,1320],{"class":65},[55,2092,95],{"class":89},[55,2094,2095,2097,2099],{"class":57,"line":126},[55,2096,90],{"class":89},[55,2098,1329],{"class":65},[55,2100,95],{"class":89},[55,2102,2103],{"class":57,"line":136},[55,2104,139],{"class":82},[55,2106,2107],{"class":57,"line":142},[55,2108,73],{"emptyLinePlaceholder":72},[55,2110,2111,2113,2116,2118],{"class":57,"line":147},[55,2112,868],{"class":61},[55,2114,2115],{"class":65}," Crawler",[55,2117,1349],{"class":61},[55,2119,298],{"class":82},[55,2121,2122,2125,2127,2130,2132],{"class":57,"line":159},[55,2123,2124],{"class":82},"    client    ",[55,2126,361],{"class":61},[55,2128,2129],{"class":65},"http",[55,2131,41],{"class":82},[55,2133,2134],{"class":65},"Client\n",[55,2136,2137,2140],{"class":57,"line":192},[55,2138,2139],{"class":82},"    workers   ",[55,2141,2142],{"class":61},"int\n",[55,2144,2145,2148],{"class":57,"line":223},[55,2146,2147],{"class":82},"    maxDepth  ",[55,2149,2142],{"class":61},[55,2151,2152,2155,2157,2159,2162],{"class":57,"line":251},[55,2153,2154],{"class":82},"    visited   ",[55,2156,1320],{"class":65},[55,2158,41],{"class":82},[55,2160,2161],{"class":65},"Map",[55,2163,2164],{"class":1132},"  \u002F\u002F URL → struct{}\n",[55,2166,2167,2170,2172,2174],{"class":57,"line":263},[55,2168,2169],{"class":82},"    rateLimit ",[55,2171,1329],{"class":65},[55,2173,41],{"class":82},[55,2175,2176],{"class":65},"Duration\n",[55,2178,2179],{"class":57,"line":268},[55,2180,820],{"class":82},[55,2182,2183],{"class":57,"line":301},[55,2184,73],{"emptyLinePlaceholder":72},[55,2186,2187,2189,2192,2194],{"class":57,"line":318},[55,2188,868],{"class":61},[55,2190,2191],{"class":65}," Result",[55,2193,1349],{"class":61},[55,2195,298],{"class":82},[55,2197,2198,2201],{"class":57,"line":334},[55,2199,2200],{"class":82},"    URL   ",[55,2202,551],{"class":61},[55,2204,2205,2208],{"class":57,"line":340},[55,2206,2207],{"class":82},"    Title ",[55,2209,551],{"class":61},[55,2211,2212,2215],{"class":57,"line":345},[55,2213,2214],{"class":82},"    Depth ",[55,2216,2142],{"class":61},[55,2218,2219,2222],{"class":57,"line":367},[55,2220,2221],{"class":82},"    Err   ",[55,2223,2224],{"class":61},"error\n",[55,2226,2227],{"class":57,"line":383},[55,2228,820],{"class":82},[55,2230,2231],{"class":57,"line":405},[55,2232,73],{"emptyLinePlaceholder":72},[55,2234,2235,2237,2239,2241,2244,2246,2249,2252,2254,2257,2259,2261,2263,2265,2267,2270],{"class":57,"line":419},[55,2236,150],{"class":61},[55,2238,1433],{"class":65},[55,2240,174],{"class":82},[55,2242,2243],{"class":876},"workers",[55,2245,34],{"class":82},[55,2247,2248],{"class":876},"maxDepth",[55,2250,2251],{"class":61}," int",[55,2253,34],{"class":82},[55,2255,2256],{"class":876},"rateLimit",[55,2258,1523],{"class":65},[55,2260,41],{"class":82},[55,2262,1528],{"class":65},[55,2264,1497],{"class":82},[55,2266,361],{"class":61},[55,2268,2269],{"class":65},"Crawler",[55,2271,298],{"class":82},[55,2273,2274,2276,2278,2280],{"class":57,"line":424},[55,2275,1072],{"class":61},[55,2277,1449],{"class":61},[55,2279,2269],{"class":65},[55,2281,2282],{"class":82},"{\n",[55,2284,2285,2288,2291,2293,2295,2298,2301,2304,2306],{"class":57,"line":438},[55,2286,2287],{"class":82},"        client:    ",[55,2289,2290],{"class":61},"&",[55,2292,2129],{"class":65},[55,2294,41],{"class":82},[55,2296,2297],{"class":65},"Client",[55,2299,2300],{"class":82},"{Timeout: ",[55,2302,2303],{"class":212},"10",[55,2305,274],{"class":61},[55,2307,2308],{"class":82}," time.Second},\n",[55,2310,2311],{"class":57,"line":443},[55,2312,2313],{"class":82},"        workers:   workers,\n",[55,2315,2316],{"class":57,"line":460},[55,2317,2318],{"class":82},"        maxDepth:  maxDepth,\n",[55,2320,2321],{"class":57,"line":476},[55,2322,2323],{"class":82},"        rateLimit: rateLimit,\n",[55,2325,2326],{"class":57,"line":489},[55,2327,337],{"class":82},[55,2329,2330],{"class":57,"line":507},[55,2331,820],{"class":82},[55,2333,2334],{"class":57,"line":520},[55,2335,73],{"emptyLinePlaceholder":72},[55,2337,2338,2340,2342,2344,2346,2348,2350,2353,2355,2357,2359,2361,2363,2365,2368,2370,2372,2375,2377],{"class":57,"line":525},[55,2339,150],{"class":61},[55,2341,905],{"class":82},[55,2343,1490],{"class":876},[55,2345,361],{"class":61},[55,2347,2269],{"class":65},[55,2349,1497],{"class":82},[55,2351,2352],{"class":65},"Crawl",[55,2354,174],{"class":82},[55,2356,1768],{"class":876},[55,2358,1771],{"class":65},[55,2360,41],{"class":82},[55,2362,1776],{"class":65},[55,2364,34],{"class":82},[55,2366,2367],{"class":876},"seed",[55,2369,1508],{"class":61},[55,2371,1497],{"class":82},[55,2373,2374],{"class":61},"\u003C-chan",[55,2376,2191],{"class":65},[55,2378,298],{"class":82},[55,2380,2381,2384,2386,2388,2390,2393,2395,2397,2400],{"class":57,"line":530},[55,2382,2383],{"class":82},"    out ",[55,2385,165],{"class":61},[55,2387,665],{"class":65},[55,2389,174],{"class":82},[55,2391,2392],{"class":61},"chan",[55,2394,2191],{"class":65},[55,2396,34],{"class":82},[55,2398,2399],{"class":212},"100",[55,2401,139],{"class":82},[55,2403,2404,2407,2409,2411,2413,2415,2418,2420,2422],{"class":57,"line":554},[55,2405,2406],{"class":82},"    jobs ",[55,2408,165],{"class":61},[55,2410,665],{"class":65},[55,2412,174],{"class":82},[55,2414,2392],{"class":61},[55,2416,2417],{"class":65}," job",[55,2419,34],{"class":82},[55,2421,2399],{"class":212},[55,2423,139],{"class":82},[55,2425,2426],{"class":57,"line":572},[55,2427,73],{"emptyLinePlaceholder":72},[55,2429,2430,2432,2435,2437,2439],{"class":57,"line":578},[55,2431,533],{"class":61},[55,2433,2434],{"class":82}," wg ",[55,2436,1320],{"class":65},[55,2438,41],{"class":82},[55,2440,2441],{"class":65},"WaitGroup\n",[55,2443,2444,2446,2448,2450,2452,2455,2457,2460,2463],{"class":57,"line":583},[55,2445,602],{"class":61},[55,2447,704],{"class":82},[55,2449,165],{"class":61},[55,2451,567],{"class":212},[55,2453,2454],{"class":82},"; i ",[55,2456,707],{"class":61},[55,2458,2459],{"class":82}," c.workers; i",[55,2461,2462],{"class":61},"++",[55,2464,298],{"class":82},[55,2466,2467,2470,2472,2474,2476],{"class":57,"line":599},[55,2468,2469],{"class":82},"        wg.",[55,2471,1573],{"class":65},[55,2473,174],{"class":82},[55,2475,414],{"class":212},[55,2477,139],{"class":82},[55,2479,2480,2483,2486],{"class":57,"line":620},[55,2481,2482],{"class":61},"        go",[55,2484,2485],{"class":61}," func",[55,2487,156],{"class":82},[55,2489,2490,2493,2496,2498],{"class":57,"line":657},[55,2491,2492],{"class":61},"            defer",[55,2494,2495],{"class":82}," wg.",[55,2497,1862],{"class":65},[55,2499,260],{"class":82},[55,2501,2502,2504,2507],{"class":57,"line":682},[55,2503,1845],{"class":82},[55,2505,2506],{"class":65},"worker",[55,2508,2509],{"class":82},"(ctx, jobs, out)\n",[55,2511,2512],{"class":57,"line":698},[55,2513,2514],{"class":82},"        }()\n",[55,2516,2517],{"class":57,"line":715},[55,2518,337],{"class":82},[55,2520,2521],{"class":57,"line":727},[55,2522,73],{"emptyLinePlaceholder":72},[55,2524,2525,2528,2530],{"class":57,"line":733},[55,2526,2527],{"class":61},"    go",[55,2529,2485],{"class":61},[55,2531,156],{"class":82},[55,2533,2534,2537,2540],{"class":57,"line":739},[55,2535,2536],{"class":61},"        defer",[55,2538,2539],{"class":65}," close",[55,2541,2542],{"class":82},"(jobs)\n",[55,2544,2545,2548,2551,2554,2556],{"class":57,"line":753},[55,2546,2547],{"class":82},"        c.",[55,2549,2550],{"class":65},"enqueue",[55,2552,2553],{"class":82},"(ctx, jobs, seed, ",[55,2555,213],{"class":212},[55,2557,139],{"class":82},[55,2559,2560],{"class":57,"line":759},[55,2561,2562],{"class":82},"    }()\n",[55,2564,2565],{"class":57,"line":764},[55,2566,73],{"emptyLinePlaceholder":72},[55,2568,2569,2571,2573],{"class":57,"line":769},[55,2570,2527],{"class":61},[55,2572,2485],{"class":61},[55,2574,156],{"class":82},[55,2576,2577,2579,2582],{"class":57,"line":786},[55,2578,2469],{"class":82},[55,2580,2581],{"class":65},"Wait",[55,2583,260],{"class":82},[55,2585,2586,2589],{"class":57,"line":806},[55,2587,2588],{"class":65},"        close",[55,2590,2591],{"class":82},"(out)\n",[55,2593,2594],{"class":57,"line":817},[55,2595,2562],{"class":82},[55,2597,2598],{"class":57,"line":1889},[55,2599,73],{"emptyLinePlaceholder":72},[55,2601,2602,2604],{"class":57,"line":1908},[55,2603,1072],{"class":61},[55,2605,2606],{"class":82}," out\n",[55,2608,2609],{"class":57,"line":1922},[55,2610,820],{"class":82},[55,2612,2613],{"class":57,"line":1931},[55,2614,73],{"emptyLinePlaceholder":72},[55,2616,2617,2619,2621,2623],{"class":57,"line":1942},[55,2618,868],{"class":61},[55,2620,2417],{"class":65},[55,2622,1349],{"class":61},[55,2624,298],{"class":82},[55,2626,2627,2630],{"class":57,"line":1957},[55,2628,2629],{"class":82},"    url   ",[55,2631,551],{"class":61},[55,2633,2634,2637],{"class":57,"line":1969},[55,2635,2636],{"class":82},"    depth ",[55,2638,2142],{"class":61},[55,2640,2641],{"class":57,"line":1978},[55,2642,820],{"class":82},[55,2644,2645],{"class":57,"line":1983},[55,2646,73],{"emptyLinePlaceholder":72},[55,2648,2649,2651,2653,2655,2657,2659,2661,2663,2665,2667,2669,2671,2673,2675,2678,2681,2683,2685,2688,2690,2692,2695,2697],{"class":57,"line":1988},[55,2650,150],{"class":61},[55,2652,905],{"class":82},[55,2654,1490],{"class":876},[55,2656,361],{"class":61},[55,2658,2269],{"class":65},[55,2660,1497],{"class":82},[55,2662,2550],{"class":65},[55,2664,174],{"class":82},[55,2666,1768],{"class":876},[55,2668,1771],{"class":65},[55,2670,41],{"class":82},[55,2672,1776],{"class":65},[55,2674,34],{"class":82},[55,2676,2677],{"class":876},"jobs",[55,2679,2680],{"class":61}," chan\u003C-",[55,2682,2417],{"class":65},[55,2684,34],{"class":82},[55,2686,2687],{"class":876},"u",[55,2689,1508],{"class":61},[55,2691,34],{"class":82},[55,2693,2694],{"class":876},"depth",[55,2696,2251],{"class":61},[55,2698,933],{"class":82},[55,2700,2702,2704,2707,2709],{"class":57,"line":2701},67,[55,2703,271],{"class":61},[55,2705,2706],{"class":82}," depth ",[55,2708,631],{"class":61},[55,2710,2711],{"class":82}," c.maxDepth {\n",[55,2713,2715],{"class":57,"line":2714},68,[55,2716,575],{"class":61},[55,2718,2720],{"class":57,"line":2719},69,[55,2721,337],{"class":82},[55,2723,2725,2727,2730,2732,2735,2738,2741,2743],{"class":57,"line":2724},70,[55,2726,271],{"class":61},[55,2728,2729],{"class":82}," _, loaded ",[55,2731,165],{"class":61},[55,2733,2734],{"class":82}," c.visited.",[55,2736,2737],{"class":65},"LoadOrStore",[55,2739,2740],{"class":82},"(u, ",[55,2742,886],{"class":61},[55,2744,2745],{"class":82},"{}{}); loaded {\n",[55,2747,2749,2751],{"class":57,"line":2748},71,[55,2750,1015],{"class":61},[55,2752,2753],{"class":1132},"  \u002F\u002F already visited\n",[55,2755,2757],{"class":57,"line":2756},72,[55,2758,337],{"class":82},[55,2760,2762,2765],{"class":57,"line":2761},73,[55,2763,2764],{"class":61},"    select",[55,2766,298],{"class":82},[55,2768,2770,2773,2776,2779],{"class":57,"line":2769},74,[55,2771,2772],{"class":61},"    case",[55,2774,2775],{"class":82}," jobs ",[55,2777,2778],{"class":61},"\u003C-",[55,2780,2781],{"class":82}," job{url: u, depth: depth}:\n",[55,2783,2785,2787,2789,2791,2793],{"class":57,"line":2784},75,[55,2786,2772],{"class":61},[55,2788,1837],{"class":61},[55,2790,1859],{"class":82},[55,2792,1862],{"class":65},[55,2794,1865],{"class":82},[55,2796,2798],{"class":57,"line":2797},76,[55,2799,337],{"class":82},[55,2801,2803],{"class":57,"line":2802},77,[55,2804,820],{"class":82},[55,2806,2808],{"class":57,"line":2807},78,[55,2809,73],{"emptyLinePlaceholder":72},[55,2811,2813,2815,2817,2819,2821,2823,2825,2827,2829,2831,2833,2835,2837,2839,2841,2844,2846,2848,2851,2853,2855],{"class":57,"line":2812},79,[55,2814,150],{"class":61},[55,2816,905],{"class":82},[55,2818,1490],{"class":876},[55,2820,361],{"class":61},[55,2822,2269],{"class":65},[55,2824,1497],{"class":82},[55,2826,2506],{"class":65},[55,2828,174],{"class":82},[55,2830,1768],{"class":876},[55,2832,1771],{"class":65},[55,2834,41],{"class":82},[55,2836,1776],{"class":65},[55,2838,34],{"class":82},[55,2840,2677],{"class":876},[55,2842,2843],{"class":61}," \u003C-chan",[55,2845,2417],{"class":65},[55,2847,34],{"class":82},[55,2849,2850],{"class":876},"out",[55,2852,2680],{"class":61},[55,2854,2191],{"class":65},[55,2856,933],{"class":82},[55,2858,2860,2862],{"class":57,"line":2859},80,[55,2861,602],{"class":61},[55,2863,298],{"class":82},[55,2865,2867,2869],{"class":57,"line":2866},81,[55,2868,1827],{"class":61},[55,2870,298],{"class":82},[55,2872,2874,2876,2879,2881,2883],{"class":57,"line":2873},82,[55,2875,1834],{"class":61},[55,2877,2878],{"class":82}," j, ok ",[55,2880,165],{"class":61},[55,2882,1837],{"class":61},[55,2884,2885],{"class":82},"jobs:\n",[55,2887,2889,2892,2894],{"class":57,"line":2888},83,[55,2890,2891],{"class":61},"            if",[55,2893,1653],{"class":61},[55,2895,1656],{"class":82},[55,2897,2899],{"class":57,"line":2898},84,[55,2900,2901],{"class":61},"                return\n",[55,2903,2905],{"class":57,"line":2904},85,[55,2906,736],{"class":82},[55,2908,2910,2913,2915,2918,2921],{"class":57,"line":2909},86,[55,2911,2912],{"class":82},"            result, links ",[55,2914,165],{"class":61},[55,2916,2917],{"class":82}," c.",[55,2919,2920],{"class":65},"fetch",[55,2922,2923],{"class":82},"(ctx, j)\n",[55,2925,2927,2930],{"class":57,"line":2926},87,[55,2928,2929],{"class":61},"            select",[55,2931,298],{"class":82},[55,2933,2935,2938,2941,2943],{"class":57,"line":2934},88,[55,2936,2937],{"class":61},"            case",[55,2939,2940],{"class":82}," out ",[55,2942,2778],{"class":61},[55,2944,2945],{"class":82}," result:\n",[55,2947,2949,2951,2953,2955,2957],{"class":57,"line":2948},89,[55,2950,2937],{"class":61},[55,2952,1837],{"class":61},[55,2954,1859],{"class":82},[55,2956,1862],{"class":65},[55,2958,1865],{"class":82},[55,2960,2962],{"class":57,"line":2961},90,[55,2963,2901],{"class":61},[55,2965,2967],{"class":57,"line":2966},91,[55,2968,736],{"class":82},[55,2970,2972,2974,2977,2979,2981],{"class":57,"line":2971},92,[55,2973,685],{"class":61},[55,2975,2976],{"class":82}," _, link ",[55,2978,165],{"class":61},[55,2980,610],{"class":61},[55,2982,2983],{"class":82}," links {\n",[55,2985,2987,2990,2992,2995,2998,3000],{"class":57,"line":2986},93,[55,2988,2989],{"class":82},"                c.",[55,2991,2550],{"class":65},[55,2993,2994],{"class":82},"(ctx, jobs, link, j.depth",[55,2996,2997],{"class":61},"+",[55,2999,414],{"class":212},[55,3001,139],{"class":82},[55,3003,3005],{"class":57,"line":3004},94,[55,3006,736],{"class":82},[55,3008,3010,3012,3014,3016,3018],{"class":57,"line":3009},95,[55,3011,1834],{"class":61},[55,3013,1837],{"class":61},[55,3015,1859],{"class":82},[55,3017,1862],{"class":65},[55,3019,1865],{"class":82},[55,3021,3023],{"class":57,"line":3022},96,[55,3024,1870],{"class":61},[55,3026,3028],{"class":57,"line":3027},97,[55,3029,756],{"class":82},[55,3031,3033],{"class":57,"line":3032},98,[55,3034,337],{"class":82},[55,3036,3038],{"class":57,"line":3037},99,[55,3039,820],{"class":82},[55,3041,3043],{"class":57,"line":3042},100,[55,3044,73],{"emptyLinePlaceholder":72},[55,3046,3048,3050,3052,3054,3056,3058,3060,3062,3064,3066,3068,3070,3072,3074,3077,3079,3081,3084,3087,3089],{"class":57,"line":3047},101,[55,3049,150],{"class":61},[55,3051,905],{"class":82},[55,3053,1490],{"class":876},[55,3055,361],{"class":61},[55,3057,2269],{"class":65},[55,3059,1497],{"class":82},[55,3061,2920],{"class":65},[55,3063,174],{"class":82},[55,3065,1768],{"class":876},[55,3067,1771],{"class":65},[55,3069,41],{"class":82},[55,3071,1776],{"class":65},[55,3073,34],{"class":82},[55,3075,3076],{"class":876},"j",[55,3078,2417],{"class":65},[55,3080,1609],{"class":82},[55,3082,3083],{"class":65},"Result",[55,3085,3086],{"class":82},", []",[55,3088,545],{"class":61},[55,3090,933],{"class":82},[55,3092,3094,3097,3099,3102,3105,3108,3111,3114,3117],{"class":57,"line":3093},102,[55,3095,3096],{"class":82},"    req, err ",[55,3098,165],{"class":61},[55,3100,3101],{"class":82}," http.",[55,3103,3104],{"class":65},"NewRequestWithContext",[55,3106,3107],{"class":82},"(ctx, ",[55,3109,3110],{"class":89},"\"GET\"",[55,3112,3113],{"class":82},", j.url, ",[55,3115,3116],{"class":212},"nil",[55,3118,139],{"class":82},[55,3120,3122,3124,3126,3128,3130],{"class":57,"line":3121},103,[55,3123,271],{"class":61},[55,3125,372],{"class":82},[55,3127,375],{"class":61},[55,3129,378],{"class":212},[55,3131,298],{"class":82},[55,3133,3135,3137,3139,3142],{"class":57,"line":3134},104,[55,3136,1015],{"class":61},[55,3138,2191],{"class":65},[55,3140,3141],{"class":82},"{URL: j.url, Depth: j.depth, Err: err}, ",[55,3143,3144],{"class":212},"nil\n",[55,3146,3148],{"class":57,"line":3147},105,[55,3149,337],{"class":82},[55,3151,3153,3156,3158,3161,3164],{"class":57,"line":3152},106,[55,3154,3155],{"class":82},"    resp, err ",[55,3157,165],{"class":61},[55,3159,3160],{"class":82}," c.client.",[55,3162,3163],{"class":65},"Do",[55,3165,3166],{"class":82},"(req)\n",[55,3168,3170,3172,3174,3176,3178],{"class":57,"line":3169},107,[55,3171,271],{"class":61},[55,3173,372],{"class":82},[55,3175,375],{"class":61},[55,3177,378],{"class":212},[55,3179,298],{"class":82},[55,3181,3183,3185,3187,3189],{"class":57,"line":3182},108,[55,3184,1015],{"class":61},[55,3186,2191],{"class":65},[55,3188,3141],{"class":82},[55,3190,3144],{"class":212},[55,3192,3194],{"class":57,"line":3193},109,[55,3195,337],{"class":82},[55,3197,3199,3201,3204,3206],{"class":57,"line":3198},110,[55,3200,427],{"class":61},[55,3202,3203],{"class":82}," resp.Body.",[55,3205,433],{"class":65},[55,3207,260],{"class":82},[55,3209,3211],{"class":57,"line":3210},111,[55,3212,73],{"emptyLinePlaceholder":72},[55,3214,3216,3219,3221,3224],{"class":57,"line":3215},112,[55,3217,3218],{"class":82},"    title, links ",[55,3220,165],{"class":61},[55,3222,3223],{"class":65}," parseHTML",[55,3225,3226],{"class":82},"(resp.Body, j.url)\n",[55,3228,3230,3232,3234],{"class":57,"line":3229},113,[55,3231,1072],{"class":61},[55,3233,2191],{"class":65},[55,3235,3236],{"class":82},"{URL: j.url, Title: title, Depth: j.depth}, links\n",[55,3238,3240],{"class":57,"line":3239},114,[55,3241,820],{"class":82},[23,3243,3245],{"id":3244},"_11-json-streaming-server","11. JSON Streaming Server",[14,3247,3248,3249,34,3252,3255],{},"Build an HTTP server that streams large JSON responses (NDJSON) without buffering. Use ",[31,3250,3251],{},"json.Encoder",[31,3253,3254],{},"flusher.Flush()"," for chunked transfer.",[23,3257,3259],{"id":3258},"_12-benchmark-driven-optimization","12. Benchmark-Driven Optimization",[14,3261,3262,3263,3266,3267,34,3270,3273],{},"Write a function that parses a custom log format. Benchmark it, profile with ",[31,3264,3265],{},"pprof",", and reduce allocations to zero. Use ",[31,3268,3269],{},"sync.Pool",[31,3271,3272],{},"strings.Builder",", and pre-allocation.",[23,3275,3277],{"id":3276},"_13-graceful-http-server","13. Graceful HTTP Server",[14,3279,3280,3281,831,3284,41],{},"Build an HTTP server that shuts down gracefully on SIGINT\u002FSIGTERM — drains in-flight requests, closes idle connections, and stops accepting new ones. Use ",[31,3282,3283],{},"signal.NotifyContext",[31,3285,3286],{},"http.Server.Shutdown",[43,3288,3289,3723,3727,3731,3756,3760,3763,3767,3777,3785,3811,3815,3818,3842,3846,3911,3915],{"language":45},[47,3290,3292],{"className":49,"code":3291,"language":45,"meta":51,"style":51},"package main\n\nimport (\n    \"context\"\n    \"log\"\n    \"net\u002Fhttp\"\n    \"os\u002Fsignal\"\n    \"syscall\"\n    \"time\"\n)\n\nfunc main() {\n    srv := &http.Server{\n        Addr:         \":8080\",\n        Handler:      mux(),\n        ReadTimeout:  5 * time.Second,\n        WriteTimeout: 10 * time.Second,\n        IdleTimeout:  120 * time.Second,\n    }\n\n    ctx, stop := signal.NotifyContext(context.Background(),\n        syscall.SIGINT, syscall.SIGTERM)\n    defer stop()\n\n    go func() {\n        log.Printf(\"listening on %s\", srv.Addr)\n        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n            log.Fatalf(\"listen: %v\", err)\n        }\n    }()\n\n    \u003C-ctx.Done()\n    log.Println(\"shutdown signal received\")\n\n    shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n    defer cancel()\n\n    if err := srv.Shutdown(shutdownCtx); err != nil {\n        log.Printf(\"forced shutdown: %v\", err)\n    }\n    log.Println(\"server stopped\")\n}\n",[31,3293,3294,3300,3304,3310,3318,3327,3335,3344,3353,3361,3365,3369,3377,3395,3406,3417,3430,3441,3453,3457,3461,3482,3487,3496,3500,3508,3529,3560,3580,3584,3588,3592,3603,3618,3622,3650,3659,3663,3685,3702,3706,3719],{"__ignoreMap":51},[55,3295,3296,3298],{"class":57,"line":58},[55,3297,62],{"class":61},[55,3299,66],{"class":65},[55,3301,3302],{"class":57,"line":69},[55,3303,73],{"emptyLinePlaceholder":72},[55,3305,3306,3308],{"class":57,"line":76},[55,3307,79],{"class":61},[55,3309,83],{"class":82},[55,3311,3312,3314,3316],{"class":57,"line":86},[55,3313,90],{"class":89},[55,3315,2064],{"class":65},[55,3317,95],{"class":89},[55,3319,3320,3322,3325],{"class":57,"line":98},[55,3321,90],{"class":89},[55,3323,3324],{"class":65},"log",[55,3326,95],{"class":89},[55,3328,3329,3331,3333],{"class":57,"line":107},[55,3330,90],{"class":89},[55,3332,2073],{"class":65},[55,3334,95],{"class":89},[55,3336,3337,3339,3342],{"class":57,"line":116},[55,3338,90],{"class":89},[55,3340,3341],{"class":65},"os\u002Fsignal",[55,3343,95],{"class":89},[55,3345,3346,3348,3351],{"class":57,"line":126},[55,3347,90],{"class":89},[55,3349,3350],{"class":65},"syscall",[55,3352,95],{"class":89},[55,3354,3355,3357,3359],{"class":57,"line":136},[55,3356,90],{"class":89},[55,3358,1329],{"class":65},[55,3360,95],{"class":89},[55,3362,3363],{"class":57,"line":142},[55,3364,139],{"class":82},[55,3366,3367],{"class":57,"line":147},[55,3368,73],{"emptyLinePlaceholder":72},[55,3370,3371,3373,3375],{"class":57,"line":159},[55,3372,150],{"class":61},[55,3374,153],{"class":65},[55,3376,156],{"class":82},[55,3378,3379,3382,3384,3386,3388,3390,3393],{"class":57,"line":192},[55,3380,3381],{"class":82},"    srv ",[55,3383,165],{"class":61},[55,3385,1449],{"class":61},[55,3387,2129],{"class":65},[55,3389,41],{"class":82},[55,3391,3392],{"class":65},"Server",[55,3394,2282],{"class":82},[55,3396,3397,3400,3403],{"class":57,"line":223},[55,3398,3399],{"class":82},"        Addr:         ",[55,3401,3402],{"class":89},"\":8080\"",[55,3404,3405],{"class":82},",\n",[55,3407,3408,3411,3414],{"class":57,"line":251},[55,3409,3410],{"class":82},"        Handler:      ",[55,3412,3413],{"class":65},"mux",[55,3415,3416],{"class":82},"(),\n",[55,3418,3419,3422,3425,3427],{"class":57,"line":263},[55,3420,3421],{"class":82},"        ReadTimeout:  ",[55,3423,3424],{"class":212},"5",[55,3426,274],{"class":61},[55,3428,3429],{"class":82}," time.Second,\n",[55,3431,3432,3435,3437,3439],{"class":57,"line":268},[55,3433,3434],{"class":82},"        WriteTimeout: ",[55,3436,2303],{"class":212},[55,3438,274],{"class":61},[55,3440,3429],{"class":82},[55,3442,3443,3446,3449,3451],{"class":57,"line":301},[55,3444,3445],{"class":82},"        IdleTimeout:  ",[55,3447,3448],{"class":212},"120",[55,3450,274],{"class":61},[55,3452,3429],{"class":82},[55,3454,3455],{"class":57,"line":318},[55,3456,337],{"class":82},[55,3458,3459],{"class":57,"line":334},[55,3460,73],{"emptyLinePlaceholder":72},[55,3462,3463,3466,3468,3471,3474,3477,3480],{"class":57,"line":340},[55,3464,3465],{"class":82},"    ctx, stop ",[55,3467,165],{"class":61},[55,3469,3470],{"class":82}," signal.",[55,3472,3473],{"class":65},"NotifyContext",[55,3475,3476],{"class":82},"(context.",[55,3478,3479],{"class":65},"Background",[55,3481,3416],{"class":82},[55,3483,3484],{"class":57,"line":345},[55,3485,3486],{"class":82},"        syscall.SIGINT, syscall.SIGTERM)\n",[55,3488,3489,3491,3494],{"class":57,"line":367},[55,3490,427],{"class":61},[55,3492,3493],{"class":65}," stop",[55,3495,260],{"class":82},[55,3497,3498],{"class":57,"line":383},[55,3499,73],{"emptyLinePlaceholder":72},[55,3501,3502,3504,3506],{"class":57,"line":405},[55,3503,2527],{"class":61},[55,3505,2485],{"class":61},[55,3507,156],{"class":82},[55,3509,3510,3513,3516,3518,3521,3524,3526],{"class":57,"line":419},[55,3511,3512],{"class":82},"        log.",[55,3514,3515],{"class":65},"Printf",[55,3517,174],{"class":82},[55,3519,3520],{"class":89},"\"listening on ",[55,3522,3523],{"class":212},"%s",[55,3525,399],{"class":89},[55,3527,3528],{"class":82},", srv.Addr)\n",[55,3530,3531,3533,3535,3537,3540,3543,3546,3548,3550,3553,3555,3557],{"class":57,"line":424},[55,3532,623],{"class":61},[55,3534,372],{"class":82},[55,3536,165],{"class":61},[55,3538,3539],{"class":82}," srv.",[55,3541,3542],{"class":65},"ListenAndServe",[55,3544,3545],{"class":82},"(); err ",[55,3547,375],{"class":61},[55,3549,378],{"class":212},[55,3551,3552],{"class":61}," &&",[55,3554,372],{"class":82},[55,3556,375],{"class":61},[55,3558,3559],{"class":82}," http.ErrServerClosed {\n",[55,3561,3562,3565,3568,3570,3573,3576,3578],{"class":57,"line":438},[55,3563,3564],{"class":82},"            log.",[55,3566,3567],{"class":65},"Fatalf",[55,3569,174],{"class":82},[55,3571,3572],{"class":89},"\"listen: ",[55,3574,3575],{"class":212},"%v",[55,3577,399],{"class":89},[55,3579,402],{"class":82},[55,3581,3582],{"class":57,"line":443},[55,3583,756],{"class":82},[55,3585,3586],{"class":57,"line":460},[55,3587,2562],{"class":82},[55,3589,3590],{"class":57,"line":476},[55,3591,73],{"emptyLinePlaceholder":72},[55,3593,3594,3597,3599,3601],{"class":57,"line":489},[55,3595,3596],{"class":61},"    \u003C-",[55,3598,1859],{"class":82},[55,3600,1862],{"class":65},[55,3602,260],{"class":82},[55,3604,3605,3608,3611,3613,3616],{"class":57,"line":507},[55,3606,3607],{"class":82},"    log.",[55,3609,3610],{"class":65},"Println",[55,3612,174],{"class":82},[55,3614,3615],{"class":89},"\"shutdown signal received\"",[55,3617,139],{"class":82},[55,3619,3620],{"class":57,"line":520},[55,3621,73],{"emptyLinePlaceholder":72},[55,3623,3624,3627,3629,3632,3635,3637,3639,3642,3645,3647],{"class":57,"line":525},[55,3625,3626],{"class":82},"    shutdownCtx, cancel ",[55,3628,165],{"class":61},[55,3630,3631],{"class":82}," context.",[55,3633,3634],{"class":65},"WithTimeout",[55,3636,3476],{"class":82},[55,3638,3479],{"class":65},[55,3640,3641],{"class":82},"(), ",[55,3643,3644],{"class":212},"30",[55,3646,361],{"class":61},[55,3648,3649],{"class":82},"time.Second)\n",[55,3651,3652,3654,3657],{"class":57,"line":530},[55,3653,427],{"class":61},[55,3655,3656],{"class":65}," cancel",[55,3658,260],{"class":82},[55,3660,3661],{"class":57,"line":554},[55,3662,73],{"emptyLinePlaceholder":72},[55,3664,3665,3667,3669,3671,3673,3676,3679,3681,3683],{"class":57,"line":572},[55,3666,271],{"class":61},[55,3668,372],{"class":82},[55,3670,165],{"class":61},[55,3672,3539],{"class":82},[55,3674,3675],{"class":65},"Shutdown",[55,3677,3678],{"class":82},"(shutdownCtx); err ",[55,3680,375],{"class":61},[55,3682,378],{"class":212},[55,3684,298],{"class":82},[55,3686,3687,3689,3691,3693,3696,3698,3700],{"class":57,"line":578},[55,3688,3512],{"class":82},[55,3690,3515],{"class":65},[55,3692,174],{"class":82},[55,3694,3695],{"class":89},"\"forced shutdown: ",[55,3697,3575],{"class":212},[55,3699,399],{"class":89},[55,3701,402],{"class":82},[55,3703,3704],{"class":57,"line":583},[55,3705,337],{"class":82},[55,3707,3708,3710,3712,3714,3717],{"class":57,"line":599},[55,3709,3607],{"class":82},[55,3711,3610],{"class":65},[55,3713,174],{"class":82},[55,3715,3716],{"class":89},"\"server stopped\"",[55,3718,139],{"class":82},[55,3720,3721],{"class":57,"line":620},[55,3722,820],{"class":82},[18,3724,3726],{"id":3725},"expert","Expert",[23,3728,3730],{"id":3729},"_14-generic-result-type","14. Generic Result Type",[14,3732,3733,3734,3737,3738,34,3741,34,3744,34,3747,34,3749,3752,3753,41],{},"Implement a ",[31,3735,3736],{},"Result[T]"," type (like Rust's Result) with ",[31,3739,3740],{},"Ok",[31,3742,3743],{},"Err",[31,3745,3746],{},"Unwrap",[31,3748,2161],{},[31,3750,3751],{},"AndThen",". Use generics. Write property-based tests with ",[31,3754,3755],{},"testing\u002Fquick",[23,3757,3759],{"id":3758},"_15-concurrent-merge-sort","15. Concurrent Merge Sort",[14,3761,3762],{},"Implement merge sort using goroutines for the recursive splits. Use a worker pool to limit concurrency. Benchmark against the sequential version — find the crossover point where parallelism helps.",[23,3764,3766],{"id":3765},"_16-memory-efficient-ring-buffer","16. Memory-Efficient Ring Buffer",[14,3768,3769,3770,3773,3774,3776],{},"Implement a lock-free ring buffer using ",[31,3771,3772],{},"atomic"," operations. Benchmark with multiple producers and consumers. Use ",[31,3775,3265],{}," to verify zero allocations in the hot path.",[23,3778,3780,3781,3784],{"id":3779},"_17-custom-jsonmarshaler-for-money","17. Custom ",[31,3782,3783],{},"json.Marshaler"," for Money",[14,3786,3733,3787,3790,3791,3794,3795,34,3798,3801,3802,34,3804,34,3807,3810],{},[31,3788,3789],{},"Money"," type that serializes as a string (",[31,3792,3793],{},"\"$12.34\"",") to avoid float precision issues. Support ",[31,3796,3797],{},"MarshalJSON",[31,3799,3800],{},"UnmarshalJSON",", and arithmetic (",[31,3803,1573],{},[31,3805,3806],{},"Sub",[31,3808,3809],{},"Mul",") with overflow checking.",[23,3812,3814],{"id":3813},"_18-distributed-task-queue-capstone","18. Distributed Task Queue (Capstone)",[14,3816,3817],{},"Build a task queue with:",[2003,3819,3820,3827,3830,3833,3836,3839],{},[2006,3821,3822,3823,3826],{},"A Redis-backed queue (using ",[31,3824,3825],{},"go-redis",")",[2006,3828,3829],{},"Worker processes that claim and execute tasks",[2006,3831,3832],{},"At-least-once delivery (tasks are requeued if a worker dies)",[2006,3834,3835],{},"Dead letter queue for failed tasks",[2006,3837,3838],{},"Metrics (Prometheus) for throughput, latency, error rate",[2006,3840,3841],{},"Graceful shutdown with in-flight task draining",[18,3843,3845],{"id":3844},"how-to-approach-these-exercises","How to Approach These Exercises",[3847,3848,3849,3856,3864,3870,3883,3895,3903],"ol",{},[2006,3850,3851,3855],{},[3852,3853,3854],"strong",{},"Start with a test"," — write the test first, then implement. This clarifies the interface.",[2006,3857,3858,3863],{},[3852,3859,3860,3861],{},"Use ",[31,3862,1215],{}," — every long-running function should accept and respect cancellation.",[2006,3865,3866,3869],{},[3852,3867,3868],{},"Check errors"," — never ignore an error. At minimum, log it.",[2006,3871,3872,3875,3876,831,3879,3882],{},[3852,3873,3874],{},"Benchmark hot paths"," — use ",[31,3877,3878],{},"b.ReportAllocs()",[31,3880,3881],{},"benchstat"," to verify improvements.",[2006,3884,3885,3894],{},[3852,3886,3887,3888,831,3891],{},"Run ",[31,3889,3890],{},"go vet",[31,3892,3893],{},"golangci-lint"," — catch issues before they become bugs.",[2006,3896,3897,3902],{},[3852,3898,3860,3899],{},[31,3900,3901],{},"go test -race"," — catch data races in concurrent code.",[2006,3904,3905,3910],{},[3852,3906,3907,3908],{},"Profile with ",[31,3909,3265],{}," — find the actual bottleneck before optimizing.",[18,3912,3914],{"id":3913},"whats-next","📚 What's Next",[14,3916,3917,3918,3925,3926,3931,3932,3937],{},"→ ",[3919,3920,3924],"a",{"href":3921,"rel":3922},"https:\u002F\u002Fgo.dev\u002Fdoc\u002F",[3923],"nofollow","Go Documentation"," — the official Go documentation for deeper reference.\n→ ",[3919,3927,3930],{"href":3928,"rel":3929},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Feffective_go",[3923],"Effective Go"," — idiomatic Go patterns.\n→ ",[3919,3933,3936],{"href":3934,"rel":3935},"https:\u002F\u002Fgobyexample.com\u002F",[3923],"Go by Example"," — hands-on examples for every feature.",[3939,3940,3941],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}",{"title":51,"searchDepth":69,"depth":69,"links":3943},[3944],{"id":20,"depth":69,"text":21,"children":3945},[3946,3947],{"id":25,"depth":76,"text":26},{"id":823,"depth":76,"text":824},"Production-graded exercises from beginner to expert — covering generics, concurrency patterns, I\u002FO streaming, error handling, and a capstone concurrent web crawler.","md",{},"\u002Fgo\u002F28-exercises-and-projects",{"title":5,"description":3948},"go\u002F28-exercises-and-projects","rNkugr5fwclzbU0eC3RDT4_WYdChskwe8hwwZenvEl8",1789924648812]