[{"data":1,"prerenderedAt":3142},["ShallowReactive",2],{"navigation":3,"\u002Fblog\u002Fmaps-em-go-zero-value-ordem-de-iteracao-e-concorrencia":26},[4],{"title":5,"path":6,"stem":7,"children":8,"page":25},"Blog","\u002Fblog","blog",[9,13,17,21],{"title":10,"path":11,"stem":12},"Array ou slice? Capacidade, crescimento e as armadilhas do append","\u002Fblog\u002Farrays-e-slices-em-go-capacidade-crescimento-e-append","blog\u002Farrays-e-slices-em-go-capacidade-crescimento-e-append",{"title":14,"path":15,"stem":16},"Stack ou heap? Entendendo o escape analysis do Go","\u002Fblog\u002Fescape-analysis-em-go-stack-ou-heap","blog\u002Fescape-analysis-em-go-stack-ou-heap",{"title":18,"path":19,"stem":20},"Por que Go copia tudo? Zero Values e Pointer Semantics","\u002Fblog\u002Fgo-zero-values-e-value-semantics-vs-pointer-semantics","blog\u002Fgo-zero-values-e-value-semantics-vs-pointer-semantics",{"title":22,"path":23,"stem":24},"Nil map ou map vazio? Zero value, ordem de iteração e concorrência em Go","\u002Fblog\u002Fmaps-em-go-zero-value-ordem-de-iteracao-e-concorrencia","blog\u002Fmaps-em-go-zero-value-ordem-de-iteracao-e-concorrencia",false,{"id":27,"title":22,"author":28,"body":29,"date":3135,"description":3136,"extension":3137,"image":3138,"meta":3139,"minRead":2324,"navigation":116,"path":23,"seo":3140,"stem":24,"tags":28,"updatedAt":28,"__hash__":3141},"blog\u002Fblog\u002Fmaps-em-go-zero-value-ordem-de-iteracao-e-concorrencia.md",null,{"type":30,"value":31,"toc":3121},"minimark",[32,51,54,213,219,260,268,276,291,303,308,314,336,349,364,371,386,390,399,417,420,597,603,616,657,662,672,695,698,759,776,959,973,1015,1019,1025,1147,1153,1159,1176,1185,1195,1198,1233,1238,1274,1283,1289,1294,1308,1480,1486,1495,1515,1624,1630,1639,1666,1670,1673,1692,1699,1702,1929,1935,1942,1948,1976,1979,1991,2021,2025,2032,2052,2065,2327,2332,2356,2359,2365,2380,2393,2408,2412,2420,2520,2526,2544,2580,2595,2603,2685,2698,2718,2722,2844,2848,2851,2930,2935,2939,3117],[33,34,35,36,41,42,46,47,50],"p",{},"No artigo sobre ",[37,38,40],"a",{"href":39},"\u002Fgo-zero-values-e-value-semantics-vs-pointer-semantics","zero values e pointer semantics"," eu mencionei de passagem que nem todo zero value é útil de imediato, e que o ",[43,44,45],"code",{},"map"," é o exemplo clássico: seu zero value é ",[43,48,49],{},"nil",", e escrever nele causa panic. Fechei o assunto em duas frases. Ele merecia mais espaço, e é este artigo aqui.",[33,52,53],{},"Vamos começar pelo bug, como de costume. Um rate limiter simples, guardado por cliente:",[55,56,61],"pre",{"className":57,"code":58,"language":59,"meta":60,"style":60},"language-go shiki shiki-themes material-theme-lighter github-light github-dark","type RateLimiter struct {\n    hits map[string]int\n}\n\nfunc (r *RateLimiter) Allow(client string) bool {\n    r.hits[client]++\n    return r.hits[client] \u003C= 100\n}\n","go","",[43,62,63,83,105,111,118,160,181,208],{"__ignoreMap":60},[64,65,68,72,76,79],"span",{"class":66,"line":67},"line",1,[64,69,71],{"class":70},"smGrS","type",[64,73,75],{"class":74},"sbgvK"," RateLimiter",[64,77,78],{"class":70}," struct",[64,80,82],{"class":81},"sP7_E"," {\n",[64,84,86,90,92,95,99,102],{"class":66,"line":85},2,[64,87,89],{"class":88},"su5hD","    hits ",[64,91,45],{"class":70},[64,93,94],{"class":81},"[",[64,96,98],{"class":97},"sbsja","string",[64,100,101],{"class":81},"]",[64,103,104],{"class":97},"int\n",[64,106,108],{"class":66,"line":107},3,[64,109,110],{"class":81},"}\n",[64,112,114],{"class":66,"line":113},4,[64,115,117],{"emptyLinePlaceholder":116},true,"\n",[64,119,121,124,127,131,134,137,140,144,147,150,153,155,158],{"class":66,"line":120},5,[64,122,123],{"class":70},"func",[64,125,126],{"class":81}," (",[64,128,130],{"class":129},"s99_P","r ",[64,132,133],{"class":70},"*",[64,135,136],{"class":74},"RateLimiter",[64,138,139],{"class":81},")",[64,141,143],{"class":142},"sGLFI"," Allow",[64,145,146],{"class":81},"(",[64,148,149],{"class":129},"client",[64,151,152],{"class":97}," string",[64,154,139],{"class":81},[64,156,157],{"class":97}," bool",[64,159,82],{"class":81},[64,161,163,166,169,172,174,176,178],{"class":66,"line":162},6,[64,164,165],{"class":88},"    r",[64,167,168],{"class":81},".",[64,170,171],{"class":88},"hits",[64,173,94],{"class":81},[64,175,149],{"class":88},[64,177,101],{"class":81},[64,179,180],{"class":70},"++\n",[64,182,184,188,191,193,195,197,199,201,204],{"class":66,"line":183},7,[64,185,187],{"class":186},"sVHd0","    return",[64,189,190],{"class":88}," r",[64,192,168],{"class":81},[64,194,171],{"class":88},[64,196,94],{"class":81},[64,198,149],{"class":88},[64,200,101],{"class":81},[64,202,203],{"class":70}," \u003C=",[64,205,207],{"class":206},"srdBf"," 100\n",[64,209,211],{"class":66,"line":210},8,[64,212,110],{"class":81},[33,214,215,216,218],{},"Nada aqui parece perigoso: um struct, um método, um ",[43,217,45],{}," como campo. O problema aparece no uso mais óbvio possível:",[55,220,222],{"className":57,"code":221,"language":59,"meta":60,"style":60},"var limiter RateLimiter\nlimiter.Allow(\"client-1\")\n",[43,223,224,235],{"__ignoreMap":60},[64,225,226,229,232],{"class":66,"line":67},[64,227,228],{"class":70},"var",[64,230,231],{"class":88}," limiter ",[64,233,234],{"class":74},"RateLimiter\n",[64,236,237,240,242,245,247,251,255,257],{"class":66,"line":85},[64,238,239],{"class":88},"limiter",[64,241,168],{"class":81},[64,243,244],{"class":142},"Allow",[64,246,146],{"class":81},[64,248,250],{"class":249},"sjJ54","\"",[64,252,254],{"class":253},"s_sjI","client-1",[64,256,250],{"class":249},[64,258,259],{"class":81},")\n",[55,261,266],{"className":262,"code":264,"language":265,"meta":60},[263],"language-text","panic: assignment to entry in nil map\n","text",[43,267,264],{"__ignoreMap":60},[33,269,270,273,274,168],{},[43,271,272],{},"var limiter RateLimiter"," é exatamente o padrão que o artigo sobre zero values elogiou: nenhuma inicialização, struct pronta para uso. Só que \"pronta para uso\" tinha uma exceção que eu não expliquei direito na época, e ela mora no campo ",[43,275,171],{},[277,278,279],"note",{},[33,280,281,282,286,287,290],{},"Tudo aqui foi verificado com ",[283,284,285],"strong",{},"Go 1.26",", rodando os exemplos localmente e lendo o código-fonte do runtime (",[43,288,289],{},"internal\u002Fruntime\u002Fmaps","). Onde o comportamento já mudou entre versões, existe uma seção específica sobre isso mais adiante.",[33,292,293,294,296,297,299,300,302],{},"A proposta deste artigo é destrinchar três coisas que costumam ser tratadas como folclore, mas têm mecanismo por trás: o que o zero value de um ",[43,295,45],{}," permite e o que ele proíbe, por que rodar duas vezes sobre o mesmo ",[43,298,45],{}," te dá ordens diferentes, e o que realmente acontece quando duas goroutines mexem no mesmo ",[43,301,45],{}," ao mesmo tempo.",[304,305,307],"h2",{"id":306},"por-baixo-uma-hash-table","Por baixo, uma hash table",[33,309,310,311,313],{},"Um ",[43,312,45],{}," em Go é, por definição, uma coleção não ordenada:",[315,316,317,323],"blockquote",{},[33,318,319,320,322],{},"\"Um map é um grupo não ordenado de elementos de um tipo, chamado tipo do elemento, indexado por um conjunto de chaves únicas de outro tipo, chamado tipo da chave. O valor de um map não inicializado é ",[43,321,49],{},".\"",[33,324,325,326],{},"— ",[37,327,331,332],{"href":328,"rel":329},"https:\u002F\u002Fgo.dev\u002Fref\u002Fspec#Map_types",[330],"nofollow","The Go Programming Language Specification, ",[333,334,335],"em",{},"Map types",[33,337,338,339,342,343,348],{},"\"Não ordenado\" ali não é um detalhe cosmético, é a raiz da Parte 2 deste artigo. Por baixo, a implementação é uma hash table, e desde o ",[283,340,341],{},"Go 1.24"," essa hash table é uma ",[37,344,347],{"href":345,"rel":346},"https:\u002F\u002Fabseil.io\u002Fabout\u002Fdesign\u002Fswisstables",[330],"Swiss Table",":",[315,350,351,357],{},[33,352,353,354,356],{},"\"Diversas melhorias de performance no runtime reduziram o overhead de CPU em 2–3% em média, em um conjunto de benchmarks representativos (...) Essas melhorias incluem uma nova implementação do ",[43,355,45],{}," embutido baseada em Swiss Tables (...)\"",[33,358,325,359],{},[37,360,363],{"href":361,"rel":362},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Fgo1.24",[330],"Go 1.24 Release Notes",[33,365,366,367,370],{},"A troca é reversível em tempo de build (",[43,368,369],{},"GOEXPERIMENT=noswissmap","), o que já é uma boa pista de que isso é implementação, não contrato: nenhum programa correto deveria depender de qual hash table roda por baixo.",[33,372,373,374,377,378,382,383,385],{},"Sobre o tipo da chave, a spec exige que ele seja comparável com ",[43,375,376],{},"==",", e isso já apareceu no ",[37,379,381],{"href":380},"\u002Farrays-e-slices-em-go-capacidade-crescimento-e-append#duas-coisas-diferentes-com-nomes-parecidos","artigo sobre arrays e slices",": é por isso que um array pode ser chave de ",[43,384,45],{}," e um slice não pode. Não vou repetir aquela discussão aqui — vale a pena ler se você pulou.",[304,387,389],{"id":388},"parte-1-o-zero-value-que-engana","Parte 1: o zero value que engana",[33,391,392,393,395,396,398],{},"O zero value de um ",[43,394,45],{}," é ",[43,397,49],{},", e a spec é direta sobre o que isso significa:",[315,400,401,410],{},[33,402,403,404,406,407,409],{},"\"Um ",[43,405,45],{}," ",[43,408,49],{}," é equivalente a um map vazio, exceto que nenhum elemento pode ser adicionado.\"",[33,411,325,412],{},[37,413,331,415],{"href":328,"rel":414},[330],[333,416,335],{},[33,418,419],{},"\"Equivalente a um map vazio, exceto\" é uma frase que vale a pena testar sem confiar de olhos fechados:",[55,421,423],{"className":57,"code":422,"language":59,"meta":60,"style":60},"var m map[string]int\n\nfmt.Println(m[\"missing\"]) \u002F\u002F 0, sem panic\nfmt.Println(len(m))       \u002F\u002F 0\nv, ok := m[\"missing\"]\nfmt.Println(v, ok)        \u002F\u002F 0 false\nfor range m {\n    \u002F\u002F nunca entra aqui\n}\ndelete(m, \"missing\") \u002F\u002F no-op, sem panic\n",[43,424,425,442,446,477,500,528,550,563,568,573],{"__ignoreMap":60},[64,426,427,429,432,434,436,438,440],{"class":66,"line":67},[64,428,228],{"class":70},[64,430,431],{"class":88}," m ",[64,433,45],{"class":70},[64,435,94],{"class":81},[64,437,98],{"class":97},[64,439,101],{"class":81},[64,441,104],{"class":97},[64,443,444],{"class":66,"line":85},[64,445,117],{"emptyLinePlaceholder":116},[64,447,448,451,453,456,458,461,463,465,468,470,473],{"class":66,"line":107},[64,449,450],{"class":88},"fmt",[64,452,168],{"class":81},[64,454,455],{"class":142},"Println",[64,457,146],{"class":81},[64,459,460],{"class":88},"m",[64,462,94],{"class":81},[64,464,250],{"class":249},[64,466,467],{"class":253},"missing",[64,469,250],{"class":249},[64,471,472],{"class":81},"])",[64,474,476],{"class":475},"sutJx"," \u002F\u002F 0, sem panic\n",[64,478,479,481,483,485,487,490,492,494,497],{"class":66,"line":113},[64,480,450],{"class":88},[64,482,168],{"class":81},[64,484,455],{"class":142},[64,486,146],{"class":81},[64,488,489],{"class":142},"len",[64,491,146],{"class":81},[64,493,460],{"class":88},[64,495,496],{"class":81},"))",[64,498,499],{"class":475},"       \u002F\u002F 0\n",[64,501,502,505,508,511,514,517,519,521,523,525],{"class":66,"line":120},[64,503,504],{"class":88},"v",[64,506,507],{"class":81},",",[64,509,510],{"class":88}," ok ",[64,512,513],{"class":70},":=",[64,515,516],{"class":88}," m",[64,518,94],{"class":81},[64,520,250],{"class":249},[64,522,467],{"class":253},[64,524,250],{"class":249},[64,526,527],{"class":81},"]\n",[64,529,530,532,534,536,538,540,542,545,547],{"class":66,"line":162},[64,531,450],{"class":88},[64,533,168],{"class":81},[64,535,455],{"class":142},[64,537,146],{"class":81},[64,539,504],{"class":88},[64,541,507],{"class":81},[64,543,544],{"class":88}," ok",[64,546,139],{"class":81},[64,548,549],{"class":475},"        \u002F\u002F 0 false\n",[64,551,552,555,558,560],{"class":66,"line":183},[64,553,554],{"class":186},"for",[64,556,557],{"class":186}," range",[64,559,431],{"class":88},[64,561,562],{"class":81},"{\n",[64,564,565],{"class":66,"line":210},[64,566,567],{"class":475},"    \u002F\u002F nunca entra aqui\n",[64,569,571],{"class":66,"line":570},9,[64,572,110],{"class":81},[64,574,576,579,581,583,585,588,590,592,594],{"class":66,"line":575},10,[64,577,578],{"class":142},"delete",[64,580,146],{"class":81},[64,582,460],{"class":88},[64,584,507],{"class":81},[64,586,587],{"class":249}," \"",[64,589,467],{"class":253},[64,591,250],{"class":249},[64,593,139],{"class":81},[64,595,596],{"class":475}," \u002F\u002F no-op, sem panic\n",[55,598,601],{"className":599,"code":600,"language":265,"meta":60},[263],"read from nil map: 0\nlen(nil map): 0\ncomma-ok on nil map: 0 false\nrange over nil map iterations: 0\ndelete on nil map: survived\n",[43,602,600],{"__ignoreMap":60},[33,604,605,606,608,609,612,613,615],{},"Leitura, ",[43,607,489],{},", ",[43,610,611],{},"range",", comma-ok e ",[43,614,578],{}," funcionam todos normalmente. A única operação que quebra é a escrita:",[55,617,619],{"className":57,"code":618,"language":59,"meta":60,"style":60},"var m map[string]int\nm[\"missing\"] = 1\n",[43,620,621,637],{"__ignoreMap":60},[64,622,623,625,627,629,631,633,635],{"class":66,"line":67},[64,624,228],{"class":70},[64,626,431],{"class":88},[64,628,45],{"class":70},[64,630,94],{"class":81},[64,632,98],{"class":97},[64,634,101],{"class":81},[64,636,104],{"class":97},[64,638,639,641,643,645,647,649,651,654],{"class":66,"line":85},[64,640,460],{"class":88},[64,642,94],{"class":81},[64,644,250],{"class":249},[64,646,467],{"class":253},[64,648,250],{"class":249},[64,650,101],{"class":81},[64,652,653],{"class":70}," =",[64,655,656],{"class":206}," 1\n",[55,658,660],{"className":659,"code":264,"language":265,"meta":60},[263],[43,661,264],{"__ignoreMap":60},[33,663,664,665,667,668,671],{},"Essa é a exceção da spec, e é ela que pegou o ",[43,666,136],{}," da introdução: ",[43,669,670],{},"r.hits[client]++"," é leitura seguida de escrita, e a escrita é o que derruba o programa.",[277,673,674],{},[33,675,676,677,406,680,682,683,686,687,689,690,406,692,694],{},"Isso é diferente de um ",[43,678,679],{},"slice",[43,681,49],{},", que aceita ",[43,684,685],{},"append"," numa boa — ponto que também já apareceu no artigo de zero values. A assimetria existe porque ",[43,688,685],{}," sempre pode criar um array novo por baixo, e o compilador sabe fazer isso. Escrever num ",[43,691,45],{},[43,693,49],{}," exigiria o mesmo truque, e o time do Go optou por não fazer alocação implícita ali; a spec faz questão de deixar isso explícito como exceção, não como acidente.",[33,696,697],{},"A correção de verdade é inicializar o campo no construtor:",[55,699,701],{"className":57,"code":700,"language":59,"meta":60,"style":60},"func NewRateLimiter() *RateLimiter {\n    return &RateLimiter{hits: make(map[string]int)}\n}\n",[43,702,703,720,755],{"__ignoreMap":60},[64,704,705,707,710,713,716,718],{"class":66,"line":67},[64,706,123],{"class":70},[64,708,709],{"class":142}," NewRateLimiter",[64,711,712],{"class":81},"()",[64,714,715],{"class":70}," *",[64,717,136],{"class":74},[64,719,82],{"class":81},[64,721,722,724,727,729,732,734,736,739,741,743,745,747,749,752],{"class":66,"line":85},[64,723,187],{"class":186},[64,725,726],{"class":70}," &",[64,728,136],{"class":74},[64,730,731],{"class":81},"{",[64,733,171],{"class":88},[64,735,348],{"class":81},[64,737,738],{"class":142}," make",[64,740,146],{"class":81},[64,742,45],{"class":70},[64,744,94],{"class":81},[64,746,98],{"class":97},[64,748,101],{"class":81},[64,750,751],{"class":97},"int",[64,753,754],{"class":81},")}\n",[64,756,757],{"class":66,"line":107},[64,758,110],{"class":81},[33,760,761,762,765,766,768,769,772,773,348],{},"Estruturas que vêm de fora, porém, às vezes te dão o ",[43,763,764],{},"make"," de graça. Um ",[43,767,45],{}," que é campo de destino de um ",[43,770,771],{},"json.Unmarshal"," é alocado automaticamente ",[283,774,775],{},"se a chave existir no JSON",[55,777,779],{"className":57,"code":778,"language":59,"meta":60,"style":60},"type Config struct {\n    Tags map[string]string\n}\n\nvar c Config\njson.Unmarshal([]byte(`{\"Tags\":{\"env\":\"prod\"}}`), &c)\nfmt.Println(c.Tags == nil) \u002F\u002F false\n\nvar c2 Config\njson.Unmarshal([]byte(`{}`), &c2)\nfmt.Println(c2.Tags == nil) \u002F\u002F true\n",[43,780,781,792,808,812,816,826,862,890,894,903,933],{"__ignoreMap":60},[64,782,783,785,788,790],{"class":66,"line":67},[64,784,71],{"class":70},[64,786,787],{"class":74}," Config",[64,789,78],{"class":70},[64,791,82],{"class":81},[64,793,794,797,799,801,803,805],{"class":66,"line":85},[64,795,796],{"class":88},"    Tags ",[64,798,45],{"class":70},[64,800,94],{"class":81},[64,802,98],{"class":97},[64,804,101],{"class":81},[64,806,807],{"class":97},"string\n",[64,809,810],{"class":66,"line":107},[64,811,110],{"class":81},[64,813,814],{"class":66,"line":113},[64,815,117],{"emptyLinePlaceholder":116},[64,817,818,820,823],{"class":66,"line":120},[64,819,228],{"class":70},[64,821,822],{"class":88}," c ",[64,824,825],{"class":74},"Config\n",[64,827,828,831,833,836,839,842,844,847,850,852,855,857,860],{"class":66,"line":162},[64,829,830],{"class":88},"json",[64,832,168],{"class":81},[64,834,835],{"class":142},"Unmarshal",[64,837,838],{"class":81},"([]",[64,840,841],{"class":97},"byte",[64,843,146],{"class":81},[64,845,846],{"class":249},"`",[64,848,849],{"class":253},"{\"Tags\":{\"env\":\"prod\"}}",[64,851,846],{"class":249},[64,853,854],{"class":81},"),",[64,856,726],{"class":70},[64,858,859],{"class":88},"c",[64,861,259],{"class":81},[64,863,864,866,868,870,872,874,876,879,881,885,887],{"class":66,"line":183},[64,865,450],{"class":88},[64,867,168],{"class":81},[64,869,455],{"class":142},[64,871,146],{"class":81},[64,873,859],{"class":88},[64,875,168],{"class":81},[64,877,878],{"class":88},"Tags ",[64,880,376],{"class":70},[64,882,884],{"class":883},"s39Yj"," nil",[64,886,139],{"class":81},[64,888,889],{"class":475}," \u002F\u002F false\n",[64,891,892],{"class":66,"line":210},[64,893,117],{"emptyLinePlaceholder":116},[64,895,896,898,901],{"class":66,"line":570},[64,897,228],{"class":70},[64,899,900],{"class":88}," c2 ",[64,902,825],{"class":74},[64,904,905,907,909,911,913,915,917,919,922,924,926,928,931],{"class":66,"line":575},[64,906,830],{"class":88},[64,908,168],{"class":81},[64,910,835],{"class":142},[64,912,838],{"class":81},[64,914,841],{"class":97},[64,916,146],{"class":81},[64,918,846],{"class":249},[64,920,921],{"class":253},"{}",[64,923,846],{"class":249},[64,925,854],{"class":81},[64,927,726],{"class":70},[64,929,930],{"class":88},"c2",[64,932,259],{"class":81},[64,934,936,938,940,942,944,946,948,950,952,954,956],{"class":66,"line":935},11,[64,937,450],{"class":88},[64,939,168],{"class":81},[64,941,455],{"class":142},[64,943,146],{"class":81},[64,945,930],{"class":88},[64,947,168],{"class":81},[64,949,878],{"class":88},[64,951,376],{"class":70},[64,953,884],{"class":883},[64,955,139],{"class":81},[64,957,958],{"class":475}," \u002F\u002F true\n",[33,960,961,962,964,965,968,969,972],{},"O que explica um bug clássico de configuração: o ",[43,963,45],{}," funciona em todo ambiente que define a chave ",[43,966,967],{},"Tags"," e panica especificamente naquele onde ela foi omitida. Não é um bug de ",[43,970,971],{},"encoding\u002Fjson",", é o comportamento documentado da spec aparecendo num lugar inesperado.",[974,975,976],"tip",{},[33,977,978,981,982,984,985,987,988,608,990,395,993,996,997,1000,1001,1004,1005,1007,1008,1011,1012,1014],{},[43,979,980],{},"clear(m)",", disponível desde o Go 1.21, esvazia um ",[43,983,45],{}," sem torná-lo ",[43,986,49],{},": depois de ",[43,989,980],{},[43,991,992],{},"len(m)",[43,994,995],{},"0",", mas ",[43,998,999],{},"m == nil"," continua ",[43,1002,1003],{},"false",". Se o seu código depois checa ",[43,1006,999],{}," para decidir se precisa inicializar, ",[43,1009,1010],{},"clear"," não serve para isso — é ",[43,1013,578],{}," de tudo, não é \"resetar para o zero value\".",[304,1016,1018],{"id":1017},"parte-2-a-ordem-de-iteração-é-aleatória-de-propósito","Parte 2: a ordem de iteração é aleatória de propósito",[33,1020,1021,1022,1024],{},"Aqui está uma demonstração simples: a mesma função, chamada cinco vezes seguidas, sobre o mesmo ",[43,1023,45],{},", no mesmo processo.",[55,1026,1028],{"className":57,"code":1027,"language":59,"meta":60,"style":60},"func keys(m map[int]bool) []int {\n    out := make([]int, 0, len(m))\n    for k := range m {\n        out = append(out, k)\n    }\n    return out\n}\n",[43,1029,1030,1062,1092,1108,1131,1136,1143],{"__ignoreMap":60},[64,1031,1032,1034,1037,1039,1041,1044,1046,1048,1050,1053,1055,1058,1060],{"class":66,"line":67},[64,1033,123],{"class":70},[64,1035,1036],{"class":142}," keys",[64,1038,146],{"class":81},[64,1040,460],{"class":129},[64,1042,1043],{"class":70}," map",[64,1045,94],{"class":81},[64,1047,751],{"class":97},[64,1049,101],{"class":81},[64,1051,1052],{"class":97},"bool",[64,1054,139],{"class":81},[64,1056,1057],{"class":81}," []",[64,1059,751],{"class":97},[64,1061,82],{"class":81},[64,1063,1064,1067,1069,1071,1073,1075,1077,1080,1082,1085,1087,1089],{"class":66,"line":85},[64,1065,1066],{"class":88},"    out ",[64,1068,513],{"class":70},[64,1070,738],{"class":142},[64,1072,838],{"class":81},[64,1074,751],{"class":97},[64,1076,507],{"class":81},[64,1078,1079],{"class":206}," 0",[64,1081,507],{"class":81},[64,1083,1084],{"class":142}," len",[64,1086,146],{"class":81},[64,1088,460],{"class":88},[64,1090,1091],{"class":81},"))\n",[64,1093,1094,1097,1100,1102,1104,1106],{"class":66,"line":107},[64,1095,1096],{"class":186},"    for",[64,1098,1099],{"class":88}," k ",[64,1101,513],{"class":70},[64,1103,557],{"class":186},[64,1105,431],{"class":88},[64,1107,562],{"class":81},[64,1109,1110,1113,1116,1119,1121,1124,1126,1129],{"class":66,"line":113},[64,1111,1112],{"class":88},"        out ",[64,1114,1115],{"class":70},"=",[64,1117,1118],{"class":142}," append",[64,1120,146],{"class":81},[64,1122,1123],{"class":88},"out",[64,1125,507],{"class":81},[64,1127,1128],{"class":88}," k",[64,1130,259],{"class":81},[64,1132,1133],{"class":66,"line":120},[64,1134,1135],{"class":81},"    }\n",[64,1137,1138,1140],{"class":66,"line":162},[64,1139,187],{"class":186},[64,1141,1142],{"class":88}," out\n",[64,1144,1145],{"class":66,"line":183},[64,1146,110],{"class":81},[55,1148,1151],{"className":1149,"code":1150,"language":265,"meta":60},[263],"[4 6 7 8 9 2 5 0 1 3]\n[1 3 4 6 7 8 9 2 5 0]\n[4 6 7 8 9 2 5 0 1 3]\n[0 1 3 4 6 7 8 9 2 5]\n[2 5 0 1 3 4 6 7 8 9]\n",[43,1152,1150],{"__ignoreMap":60},[33,1154,1155,1156,1158],{},"Cinco chamadas, quatro ordens diferentes, mesmo ",[43,1157,45],{},", mesmo processo, mesmos dados. A spec é explícita sobre isso desde a primeira versão pública da linguagem:",[315,1160,1161,1167],{},[33,1162,1163,1164,1166],{},"\"A ordem de iteração sobre maps não é especificada e não há garantia de que seja a mesma de uma iteração para a outra. Se uma entrada do map que ainda não foi alcançada é removida durante a iteração, o valor de iteração correspondente não será produzido. Se uma entrada é criada durante a iteração, ela pode ou não ser produzida durante aquela iteração. (...) Se o map é ",[43,1165,49],{},", o número de iterações é 0.\"",[33,1168,325,1169],{},[37,1170,331,1173],{"href":1171,"rel":1172},"https:\u002F\u002Fgo.dev\u002Fref\u002Fspec#For_statements",[330],[333,1174,1175],{},"For statements",[33,1177,1178,1179,1184],{},"Essa garantia (ou melhor, a ausência dela) já era documentada no ",[37,1180,1183],{"href":1181,"rel":1182},"https:\u002F\u002Fgo.dev\u002Fblog\u002Fmaps",[330],"Go Blog em 2013",", então não é novidade trazida pela Swiss Table de 2024: é uma escolha de design antiga.",[33,1186,1187,1188,1191,1192,1194],{},"Repare em algo nas cinco sequências acima: elas não são embaralhadas de forma independente, são ",[283,1189,1190],{},"rotações"," umas das outras. A segunda começa exatamente onde a primeira tinha \"1\" e continua na mesma ordem cíclica dali em diante. Isso não é coincidência, é o mecanismo: cada ",[43,1193,611],{}," sorteia só o ponto de partida, não a ordem relativa dos elementos.",[33,1196,1197],{},"A struct do iterador declara dois campos para isso:",[55,1199,1201],{"className":57,"code":1200,"language":59,"meta":60,"style":60},"\u002F\u002F Randomize iteration order by starting iteration at a random slot\n\u002F\u002F offset. The offset into the directory uses a separate offset, as it\n\u002F\u002F must adjust when the directory grows.\nentryOffset uint64\ndirOffset   uint64\n",[43,1202,1203,1208,1213,1218,1226],{"__ignoreMap":60},[64,1204,1205],{"class":66,"line":67},[64,1206,1207],{"class":475},"\u002F\u002F Randomize iteration order by starting iteration at a random slot\n",[64,1209,1210],{"class":66,"line":85},[64,1211,1212],{"class":475},"\u002F\u002F offset. The offset into the directory uses a separate offset, as it\n",[64,1214,1215],{"class":66,"line":107},[64,1216,1217],{"class":475},"\u002F\u002F must adjust when the directory grows.\n",[64,1219,1220,1223],{"class":66,"line":113},[64,1221,1222],{"class":88},"entryOffset ",[64,1224,1225],{"class":97},"uint64\n",[64,1227,1228,1231],{"class":66,"line":120},[64,1229,1230],{"class":88},"dirOffset   ",[64,1232,1225],{"class":97},[33,1234,1235,1236,348],{},"E o método que inicializa um novo iterador os preenche assim, uma vez por chamada de ",[43,1237,611],{},[55,1239,1241],{"className":57,"code":1240,"language":59,"meta":60,"style":60},"it.entryOffset = rand()\nit.dirOffset   = rand()\n",[43,1242,1243,1260],{"__ignoreMap":60},[64,1244,1245,1248,1250,1252,1254,1257],{"class":66,"line":67},[64,1246,1247],{"class":88},"it",[64,1249,168],{"class":81},[64,1251,1222],{"class":88},[64,1253,1115],{"class":70},[64,1255,1256],{"class":142}," rand",[64,1258,1259],{"class":81},"()\n",[64,1261,1262,1264,1266,1268,1270,1272],{"class":66,"line":85},[64,1263,1247],{"class":88},[64,1265,168],{"class":81},[64,1267,1230],{"class":88},[64,1269,1115],{"class":70},[64,1271,1256],{"class":142},[64,1273,1259],{"class":81},[33,1275,325,1276],{},[37,1277,1280],{"href":1278,"rel":1279},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Finternal\u002Fruntime\u002Fmaps\u002Ftable.go",[330],[43,1281,1282],{},"internal\u002Fruntime\u002Fmaps\u002Ftable.go",[33,1284,1285,1286,1288],{},"Cada chamada a ",[43,1287,611],{}," cria um iterador novo, e esse iterador sorteia dois deslocamentos: um para a posição inicial dentro do grupo de slots, outro para o índice inicial no diretório de grupos. O layout interno dos dados não muda entre uma chamada e outra, só o ponto em que a varredura começa — e é exatamente por isso que a sequência observada é uma rotação, não uma permutação aleatória completa.",[1290,1291,1293],"h3",{"id":1292},"quando-você-precisa-de-ordem-peça-ordem","Quando você precisa de ordem, peça ordem",[33,1295,1296,1297,1299,1300,1303,1304,1307],{},"A saída correta para \"eu preciso imprimir isso sempre na mesma ordem\" não é torcer para o ",[43,1298,45],{}," cooperar, é ordenar as chaves explicitamente. Desde o Go 1.23, o pacote ",[43,1301,1302],{},"maps"," devolve um iterador, e o pacote ",[43,1305,1306],{},"slices"," sabe consumi-lo:",[55,1309,1311],{"className":57,"code":1310,"language":59,"meta":60,"style":60},"totals := map[string]int{\"checkout\": 42, \"home\": 108, \"login\": 17}\n\nkeys := slices.Sorted(maps.Keys(totals))\nfor _, k := range keys {\n    fmt.Printf(\"%s: %d\\n\", k, totals[k])\n}\n",[43,1312,1313,1374,1378,1409,1429,1476],{"__ignoreMap":60},[64,1314,1315,1318,1320,1322,1324,1326,1328,1330,1332,1334,1337,1339,1341,1344,1346,1348,1351,1353,1355,1358,1360,1362,1365,1367,1369,1372],{"class":66,"line":67},[64,1316,1317],{"class":88},"totals ",[64,1319,513],{"class":70},[64,1321,1043],{"class":70},[64,1323,94],{"class":81},[64,1325,98],{"class":97},[64,1327,101],{"class":81},[64,1329,751],{"class":97},[64,1331,731],{"class":81},[64,1333,250],{"class":249},[64,1335,1336],{"class":253},"checkout",[64,1338,250],{"class":249},[64,1340,348],{"class":81},[64,1342,1343],{"class":206}," 42",[64,1345,507],{"class":81},[64,1347,587],{"class":249},[64,1349,1350],{"class":253},"home",[64,1352,250],{"class":249},[64,1354,348],{"class":81},[64,1356,1357],{"class":206}," 108",[64,1359,507],{"class":81},[64,1361,587],{"class":249},[64,1363,1364],{"class":253},"login",[64,1366,250],{"class":249},[64,1368,348],{"class":81},[64,1370,1371],{"class":206}," 17",[64,1373,110],{"class":81},[64,1375,1376],{"class":66,"line":85},[64,1377,117],{"emptyLinePlaceholder":116},[64,1379,1380,1383,1385,1388,1390,1393,1395,1397,1399,1402,1404,1407],{"class":66,"line":107},[64,1381,1382],{"class":88},"keys ",[64,1384,513],{"class":70},[64,1386,1387],{"class":88}," slices",[64,1389,168],{"class":81},[64,1391,1392],{"class":142},"Sorted",[64,1394,146],{"class":81},[64,1396,1302],{"class":88},[64,1398,168],{"class":81},[64,1400,1401],{"class":142},"Keys",[64,1403,146],{"class":81},[64,1405,1406],{"class":88},"totals",[64,1408,1091],{"class":81},[64,1410,1411,1413,1416,1418,1420,1422,1424,1427],{"class":66,"line":113},[64,1412,554],{"class":186},[64,1414,1415],{"class":88}," _",[64,1417,507],{"class":81},[64,1419,1099],{"class":88},[64,1421,513],{"class":70},[64,1423,557],{"class":186},[64,1425,1426],{"class":88}," keys ",[64,1428,562],{"class":81},[64,1430,1431,1434,1436,1439,1441,1443,1447,1450,1453,1457,1459,1461,1463,1465,1468,1470,1473],{"class":66,"line":120},[64,1432,1433],{"class":88},"    fmt",[64,1435,168],{"class":81},[64,1437,1438],{"class":142},"Printf",[64,1440,146],{"class":81},[64,1442,250],{"class":249},[64,1444,1446],{"class":1445},"swQdS","%s",[64,1448,1449],{"class":253},": ",[64,1451,1452],{"class":1445},"%d",[64,1454,1456],{"class":1455},"s_hVV","\\n",[64,1458,250],{"class":249},[64,1460,507],{"class":81},[64,1462,1128],{"class":88},[64,1464,507],{"class":81},[64,1466,1467],{"class":88}," totals",[64,1469,94],{"class":81},[64,1471,1472],{"class":88},"k",[64,1474,1475],{"class":81},"])\n",[64,1477,1478],{"class":66,"line":162},[64,1479,110],{"class":81},[55,1481,1484],{"className":1482,"code":1483,"language":265,"meta":60},[263],"checkout: 42\nhome: 108\nlogin: 17\n",[43,1485,1483],{"__ignoreMap":60},[33,1487,1488,1489,1491,1492,1494],{},"Um lugar em que isso já acontece por baixo dos panos é ",[43,1490,971],{},". Serializar um ",[43,1493,45],{}," não devolve uma ordem aleatória:",[315,1496,1497,1503],{},[33,1498,1499,1500,1502],{},"\"Valores de ",[43,1501,45],{}," são codificados como objetos JSON. (...) As chaves do map são ordenadas e usadas como chaves do objeto JSON (...)\"",[33,1504,325,1505],{},[37,1506,1509,1510,608,1512],{"href":1507,"rel":1508},"https:\u002F\u002Fpkg.go.dev\u002Fencoding\u002Fjson#Marshal",[330],"Documentação do pacote ",[43,1511,971],{},[43,1513,1514],{},"Marshal",[55,1516,1518],{"className":57,"code":1517,"language":59,"meta":60,"style":60},"m := map[string]int{\"zebra\": 1, \"apple\": 2, \"mango\": 3}\nb, _ := json.Marshal(m)\nfmt.Println(string(b))\n",[43,1519,1520,1581,1606],{"__ignoreMap":60},[64,1521,1522,1525,1527,1529,1531,1533,1535,1537,1539,1541,1544,1546,1548,1551,1553,1555,1558,1560,1562,1565,1567,1569,1572,1574,1576,1579],{"class":66,"line":67},[64,1523,1524],{"class":88},"m ",[64,1526,513],{"class":70},[64,1528,1043],{"class":70},[64,1530,94],{"class":81},[64,1532,98],{"class":97},[64,1534,101],{"class":81},[64,1536,751],{"class":97},[64,1538,731],{"class":81},[64,1540,250],{"class":249},[64,1542,1543],{"class":253},"zebra",[64,1545,250],{"class":249},[64,1547,348],{"class":81},[64,1549,1550],{"class":206}," 1",[64,1552,507],{"class":81},[64,1554,587],{"class":249},[64,1556,1557],{"class":253},"apple",[64,1559,250],{"class":249},[64,1561,348],{"class":81},[64,1563,1564],{"class":206}," 2",[64,1566,507],{"class":81},[64,1568,587],{"class":249},[64,1570,1571],{"class":253},"mango",[64,1573,250],{"class":249},[64,1575,348],{"class":81},[64,1577,1578],{"class":206}," 3",[64,1580,110],{"class":81},[64,1582,1583,1586,1588,1591,1593,1596,1598,1600,1602,1604],{"class":66,"line":85},[64,1584,1585],{"class":88},"b",[64,1587,507],{"class":81},[64,1589,1590],{"class":88}," _ ",[64,1592,513],{"class":70},[64,1594,1595],{"class":88}," json",[64,1597,168],{"class":81},[64,1599,1514],{"class":142},[64,1601,146],{"class":81},[64,1603,460],{"class":88},[64,1605,259],{"class":81},[64,1607,1608,1610,1612,1614,1616,1618,1620,1622],{"class":66,"line":107},[64,1609,450],{"class":88},[64,1611,168],{"class":81},[64,1613,455],{"class":142},[64,1615,146],{"class":81},[64,1617,98],{"class":97},[64,1619,146],{"class":81},[64,1621,1585],{"class":88},[64,1623,1091],{"class":81},[55,1625,1628],{"className":1626,"code":1627,"language":265,"meta":60},[263],"{\"apple\":2,\"mango\":3,\"zebra\":1}\n",[43,1629,1627],{"__ignoreMap":60},[33,1631,1632,1633,1635,1636,1638],{},"Rodei esse ",[43,1634,1514],{}," três vezes no mesmo processo: as três saídas vieram idênticas, em ordem alfabética. ",[43,1637,971],{}," ordena as chaves para você antes de serializar, exatamente para que o resultado seja determinístico apesar da iteração por baixo não ser.",[974,1640,1641],{},[33,1642,1643,1644,406,1646,1649,1650,1653,1654,1657,1658,1661,1662,1665],{},"Essa garantia é do pacote ",[43,1645,971],{},[283,1647,1648],{},"v1",", o que você importa com ",[43,1651,1652],{},"\"encoding\u002Fjson\"",". Existe um pacote experimental ",[43,1655,1656],{},"encoding\u002Fjson\u002Fv2"," (atrás da flag ",[43,1659,1660],{},"GOEXPERIMENT=jsonv2",", ainda não habilitado por padrão no Go 1.26) cuja documentação declara explicitamente o oposto por padrão: ordem não determinística, com uma opção ",[43,1663,1664],{},"jsonv2.Deterministic"," para religar o comportamento antigo. Se esse pacote se tornar padrão no futuro, vale reconferir esse ponto.",[304,1667,1669],{"id":1668},"parte-3-concorrência-o-que-é-seguro-e-o-que-não-é","Parte 3: concorrência — o que é seguro e o que não é",[33,1671,1672],{},"A pergunta que o time do Go já respondeu publicamente, e vale citar por inteiro porque a resposta é mais matizada do que \"use um mutex\":",[315,1674,1675,1682],{},[33,1676,1677,1678,1681],{},"\"Depois de uma longa discussão, decidiu-se que o uso típico de maps não exigia acesso seguro a partir de múltiplas goroutines, e nos casos em que exigia, o map provavelmente fazia parte de uma estrutura de dados ou computação maior que já estava sincronizada. (...) O acesso a um map só é inseguro quando há atualizações ocorrendo. Enquanto todas as goroutines estiverem apenas lendo — buscando elementos no map, inclusive iterando com um ",[43,1679,1680],{},"for range"," — e não alterando o map por atribuição ou remoção, é seguro acessá-lo concorrentemente sem sincronização.\"",[33,1683,325,1684],{},[37,1685,1688,1689],{"href":1686,"rel":1687},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Ffaq#atomic_maps",[330],"Go FAQ, ",[333,1690,1691],{},"Why are map operations not defined to be atomic?",[33,1693,1694,1695,1698],{},"Ou seja: ",[283,1696,1697],{},"leituras concorrentes são seguras, desde que não exista um escritor",". No momento em que existe uma escrita concorrente com qualquer outro acesso (leitura ou escrita), a garantia acaba.",[33,1700,1701],{},"Um cenário realista: um contador de acessos por rota, incrementado a cada request e lido por um endpoint de métricas.",[55,1703,1705],{"className":57,"code":1704,"language":59,"meta":60,"style":60},"hits := map[string]int{\"home\": 0, \"login\": 0, \"checkout\": 0}\n\ngo func() { \u002F\u002F simula requests incrementando\n    for i := 0; i \u003C 1_000_000; i++ {\n        hits[\"home\"]++\n    }\n}()\n\ngo func() { \u002F\u002F simula o endpoint de métricas lendo\n    for i := 0; i \u003C 1_000_000; i++ {\n        _ = hits[\"home\"]\n    }\n}()\n",[43,1706,1707,1762,1766,1781,1822,1839,1843,1848,1852,1865,1899,1919,1924],{"__ignoreMap":60},[64,1708,1709,1712,1714,1716,1718,1720,1722,1724,1726,1728,1730,1732,1734,1736,1738,1740,1742,1744,1746,1748,1750,1752,1754,1756,1758,1760],{"class":66,"line":67},[64,1710,1711],{"class":88},"hits ",[64,1713,513],{"class":70},[64,1715,1043],{"class":70},[64,1717,94],{"class":81},[64,1719,98],{"class":97},[64,1721,101],{"class":81},[64,1723,751],{"class":97},[64,1725,731],{"class":81},[64,1727,250],{"class":249},[64,1729,1350],{"class":253},[64,1731,250],{"class":249},[64,1733,348],{"class":81},[64,1735,1079],{"class":206},[64,1737,507],{"class":81},[64,1739,587],{"class":249},[64,1741,1364],{"class":253},[64,1743,250],{"class":249},[64,1745,348],{"class":81},[64,1747,1079],{"class":206},[64,1749,507],{"class":81},[64,1751,587],{"class":249},[64,1753,1336],{"class":253},[64,1755,250],{"class":249},[64,1757,348],{"class":81},[64,1759,1079],{"class":206},[64,1761,110],{"class":81},[64,1763,1764],{"class":66,"line":85},[64,1765,117],{"emptyLinePlaceholder":116},[64,1767,1768,1770,1773,1775,1778],{"class":66,"line":107},[64,1769,59],{"class":186},[64,1771,1772],{"class":70}," func",[64,1774,712],{"class":81},[64,1776,1777],{"class":81}," {",[64,1779,1780],{"class":475}," \u002F\u002F simula requests incrementando\n",[64,1782,1783,1785,1788,1790,1792,1795,1797,1800,1802,1805,1808,1810,1812,1814,1817,1820],{"class":66,"line":113},[64,1784,1096],{"class":186},[64,1786,1787],{"class":88}," i ",[64,1789,513],{"class":70},[64,1791,1079],{"class":206},[64,1793,1794],{"class":81},";",[64,1796,1787],{"class":88},[64,1798,1799],{"class":70},"\u003C",[64,1801,1550],{"class":206},[64,1803,1804],{"class":883},"_",[64,1806,1807],{"class":206},"000",[64,1809,1804],{"class":883},[64,1811,1807],{"class":206},[64,1813,1794],{"class":81},[64,1815,1816],{"class":88}," i",[64,1818,1819],{"class":70},"++",[64,1821,82],{"class":81},[64,1823,1824,1827,1829,1831,1833,1835,1837],{"class":66,"line":120},[64,1825,1826],{"class":88},"        hits",[64,1828,94],{"class":81},[64,1830,250],{"class":249},[64,1832,1350],{"class":253},[64,1834,250],{"class":249},[64,1836,101],{"class":81},[64,1838,180],{"class":70},[64,1840,1841],{"class":66,"line":162},[64,1842,1135],{"class":81},[64,1844,1845],{"class":66,"line":183},[64,1846,1847],{"class":81},"}()\n",[64,1849,1850],{"class":66,"line":210},[64,1851,117],{"emptyLinePlaceholder":116},[64,1853,1854,1856,1858,1860,1862],{"class":66,"line":570},[64,1855,59],{"class":186},[64,1857,1772],{"class":70},[64,1859,712],{"class":81},[64,1861,1777],{"class":81},[64,1863,1864],{"class":475}," \u002F\u002F simula o endpoint de métricas lendo\n",[64,1866,1867,1869,1871,1873,1875,1877,1879,1881,1883,1885,1887,1889,1891,1893,1895,1897],{"class":66,"line":575},[64,1868,1096],{"class":186},[64,1870,1787],{"class":88},[64,1872,513],{"class":70},[64,1874,1079],{"class":206},[64,1876,1794],{"class":81},[64,1878,1787],{"class":88},[64,1880,1799],{"class":70},[64,1882,1550],{"class":206},[64,1884,1804],{"class":883},[64,1886,1807],{"class":206},[64,1888,1804],{"class":883},[64,1890,1807],{"class":206},[64,1892,1794],{"class":81},[64,1894,1816],{"class":88},[64,1896,1819],{"class":70},[64,1898,82],{"class":81},[64,1900,1901,1904,1906,1909,1911,1913,1915,1917],{"class":66,"line":935},[64,1902,1903],{"class":88},"        _ ",[64,1905,1115],{"class":70},[64,1907,1908],{"class":88}," hits",[64,1910,94],{"class":81},[64,1912,250],{"class":249},[64,1914,1350],{"class":253},[64,1916,250],{"class":249},[64,1918,527],{"class":81},[64,1920,1922],{"class":66,"line":1921},12,[64,1923,1135],{"class":81},[64,1925,1927],{"class":66,"line":1926},13,[64,1928,1847],{"class":81},[55,1930,1933],{"className":1931,"code":1932,"language":265,"meta":60},[263],"fatal error: concurrent map read and map write\n",[43,1934,1932],{"__ignoreMap":60},[33,1936,1937,1938,1941],{},"Rodei esse programa várias vezes, sem a flag ",[43,1939,1940],{},"-race",", e ele derrubou o processo de forma consistente com essa mensagem. Se as duas goroutines escrevem (em vez de uma ler e outra escrever), a mensagem muda:",[55,1943,1946],{"className":1944,"code":1945,"language":265,"meta":60},[263],"fatal error: concurrent map writes\n",[43,1947,1945],{"__ignoreMap":60},[277,1949,1950],{},[33,1951,1952,1953,1956,1957,1960,1961,1964,1965,1968,1969,1972,1973,1975],{},"Tentei envolver esse programa num ",[43,1954,1955],{},"recover()",". Não adiantou — o processo derruba do mesmo jeito. A razão é que isso não é um ",[43,1958,1959],{},"panic",": é um ",[43,1962,1963],{},"fatal error",", gerado pela função interna ",[43,1966,1967],{},"fatal()"," do runtime, que existe justamente para condições que o runtime considera não seguras de continuar, mesmo que alguém tenha um ",[43,1970,1971],{},"recover"," no meio do caminho. ",[43,1974,1959],{}," você pode interceptar; isso, não.",[33,1977,1978],{},"O runtime detecta esses casos com uma verificação leve, presente desde o Go 1.6:",[315,1980,1981,1984],{},[33,1982,1983],{},"\"O runtime passou a ter uma detecção leve e best-effort de uso concorrente incorreto de maps. Como sempre, se uma goroutine está escrevendo em um map, nenhuma outra goroutine deveria estar lendo ou escrevendo nesse map concorrentemente. Se o runtime detecta essa condição, ele imprime um diagnóstico e derruba o programa.\"",[33,1985,325,1986],{},[37,1987,1990],{"href":1988,"rel":1989},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Fgo1.6",[330],"Go 1.6 Release Notes",[33,1992,1993,1994,1997,1998,2001,2002,2005,2006,2009,2010,1449,2015,608,2018,168],{},"Por dentro, a implementação mantém um campo (",[43,1995,1996],{},"writing",", em ",[43,1999,2000],{},"internal\u002Fruntime\u002Fmaps\u002Fmap.go",") que é alternado a cada escrita; se uma leitura ou outra escrita encontra esse campo já ligado, ela chama ",[43,2003,2004],{},"fatal",". É uma verificação leve de propósito — ",[283,2007,2008],{},"best-effort",", nas palavras da release note — não uma trava. Ela pega grande parte dos casos reais, mas não é uma prova de ausência de corrida: código que passa sem crashar não está necessariamente correto, só não teve azar naquela execução. Para uma detecção confiável, a ferramenta certa é o ",[37,2011,2014],{"href":2012,"rel":2013},"https:\u002F\u002Fgo.dev\u002Fblog\u002Frace-detector",[330],"detector de corrida",[43,2016,2017],{},"go test -race",[43,2019,2020],{},"go run -race",[1290,2022,2024],{"id":2023},"duas-ferramentas-dois-problemas-diferentes","Duas ferramentas, dois problemas diferentes",[33,2026,2027,2028,2031],{},"A resposta para \"qual mecanismo eu uso\" não é sempre a mesma. A documentação do ",[43,2029,2030],{},"sync.Map"," é direta sobre isso:",[315,2033,2034,2041],{},[33,2035,2036,2037,2040],{},"\"O tipo ",[43,2038,2039],{},"Map"," é especializado. A maior parte do código deveria usar um map comum do Go, com locking separado, para melhor segurança de tipos e para facilitar a manutenção de outras invariantes junto com o conteúdo do map.\"",[33,2042,325,2043],{},[37,2044,1509,2047,608,2050],{"href":2045,"rel":2046},"https:\u002F\u002Fpkg.go.dev\u002Fsync#Map",[330],[43,2048,2049],{},"sync",[43,2051,2039],{},[33,2053,2054,2055,2057,2058,2061,2062,348],{},"O caso padrão, então, é ",[43,2056,45],{}," comum protegido por ",[43,2059,2060],{},"sync.Mutex"," ou ",[43,2063,2064],{},"sync.RWMutex",[55,2066,2068],{"className":57,"code":2067,"language":59,"meta":60,"style":60},"type Cache struct {\n    mu sync.RWMutex\n    m  map[string]int\n}\n\nfunc (c *Cache) Get(k string) (int, bool) {\n    c.mu.RLock()\n    defer c.mu.RUnlock()\n    v, ok := c.m[k]\n    return v, ok\n}\n\nfunc (c *Cache) Set(k string, v int) {\n    c.mu.Lock()\n    defer c.mu.Unlock()\n    c.m[k] = v\n}\n",[43,2069,2070,2081,2093,2108,2112,2116,2155,2172,2191,2214,2226,2230,2234,2268,2284,2302,2322],{"__ignoreMap":60},[64,2071,2072,2074,2077,2079],{"class":66,"line":67},[64,2073,71],{"class":70},[64,2075,2076],{"class":74}," Cache",[64,2078,78],{"class":70},[64,2080,82],{"class":81},[64,2082,2083,2086,2088,2090],{"class":66,"line":85},[64,2084,2085],{"class":88},"    mu ",[64,2087,2049],{"class":74},[64,2089,168],{"class":81},[64,2091,2092],{"class":74},"RWMutex\n",[64,2094,2095,2098,2100,2102,2104,2106],{"class":66,"line":107},[64,2096,2097],{"class":88},"    m  ",[64,2099,45],{"class":70},[64,2101,94],{"class":81},[64,2103,98],{"class":97},[64,2105,101],{"class":81},[64,2107,104],{"class":97},[64,2109,2110],{"class":66,"line":113},[64,2111,110],{"class":81},[64,2113,2114],{"class":66,"line":120},[64,2115,117],{"emptyLinePlaceholder":116},[64,2117,2118,2120,2122,2125,2127,2130,2132,2135,2137,2139,2141,2143,2145,2147,2149,2151,2153],{"class":66,"line":162},[64,2119,123],{"class":70},[64,2121,126],{"class":81},[64,2123,2124],{"class":129},"c ",[64,2126,133],{"class":70},[64,2128,2129],{"class":74},"Cache",[64,2131,139],{"class":81},[64,2133,2134],{"class":142}," Get",[64,2136,146],{"class":81},[64,2138,1472],{"class":129},[64,2140,152],{"class":97},[64,2142,139],{"class":81},[64,2144,126],{"class":81},[64,2146,751],{"class":97},[64,2148,507],{"class":81},[64,2150,157],{"class":97},[64,2152,139],{"class":81},[64,2154,82],{"class":81},[64,2156,2157,2160,2162,2165,2167,2170],{"class":66,"line":183},[64,2158,2159],{"class":88},"    c",[64,2161,168],{"class":81},[64,2163,2164],{"class":88},"mu",[64,2166,168],{"class":81},[64,2168,2169],{"class":142},"RLock",[64,2171,1259],{"class":81},[64,2173,2174,2177,2180,2182,2184,2186,2189],{"class":66,"line":210},[64,2175,2176],{"class":186},"    defer",[64,2178,2179],{"class":88}," c",[64,2181,168],{"class":81},[64,2183,2164],{"class":88},[64,2185,168],{"class":81},[64,2187,2188],{"class":142},"RUnlock",[64,2190,1259],{"class":81},[64,2192,2193,2196,2198,2200,2202,2204,2206,2208,2210,2212],{"class":66,"line":570},[64,2194,2195],{"class":88},"    v",[64,2197,507],{"class":81},[64,2199,510],{"class":88},[64,2201,513],{"class":70},[64,2203,2179],{"class":88},[64,2205,168],{"class":81},[64,2207,460],{"class":88},[64,2209,94],{"class":81},[64,2211,1472],{"class":88},[64,2213,527],{"class":81},[64,2215,2216,2218,2221,2223],{"class":66,"line":575},[64,2217,187],{"class":186},[64,2219,2220],{"class":88}," v",[64,2222,507],{"class":81},[64,2224,2225],{"class":88}," ok\n",[64,2227,2228],{"class":66,"line":935},[64,2229,110],{"class":81},[64,2231,2232],{"class":66,"line":1921},[64,2233,117],{"emptyLinePlaceholder":116},[64,2235,2236,2238,2240,2242,2244,2246,2248,2251,2253,2255,2257,2259,2261,2264,2266],{"class":66,"line":1926},[64,2237,123],{"class":70},[64,2239,126],{"class":81},[64,2241,2124],{"class":129},[64,2243,133],{"class":70},[64,2245,2129],{"class":74},[64,2247,139],{"class":81},[64,2249,2250],{"class":142}," Set",[64,2252,146],{"class":81},[64,2254,1472],{"class":129},[64,2256,152],{"class":97},[64,2258,507],{"class":81},[64,2260,2220],{"class":129},[64,2262,2263],{"class":97}," int",[64,2265,139],{"class":81},[64,2267,82],{"class":81},[64,2269,2271,2273,2275,2277,2279,2282],{"class":66,"line":2270},14,[64,2272,2159],{"class":88},[64,2274,168],{"class":81},[64,2276,2164],{"class":88},[64,2278,168],{"class":81},[64,2280,2281],{"class":142},"Lock",[64,2283,1259],{"class":81},[64,2285,2287,2289,2291,2293,2295,2297,2300],{"class":66,"line":2286},15,[64,2288,2176],{"class":186},[64,2290,2179],{"class":88},[64,2292,168],{"class":81},[64,2294,2164],{"class":88},[64,2296,168],{"class":81},[64,2298,2299],{"class":142},"Unlock",[64,2301,1259],{"class":81},[64,2303,2305,2307,2309,2311,2313,2315,2317,2319],{"class":66,"line":2304},16,[64,2306,2159],{"class":88},[64,2308,168],{"class":81},[64,2310,460],{"class":88},[64,2312,94],{"class":81},[64,2314,1472],{"class":88},[64,2316,101],{"class":81},[64,2318,653],{"class":70},[64,2320,2321],{"class":88}," v\n",[64,2323,2325],{"class":66,"line":2324},17,[64,2326,110],{"class":81},[33,2328,2329,2331],{},[43,2330,2030],{}," existe para dois padrões de acesso específicos, também descritos na documentação:",[315,2333,2334,2347],{},[33,2335,2336,2337,2339,2340,2061,2343,2346],{},"\"quando a entrada de uma dada chave é escrita apenas uma vez mas lida muitas vezes, como em caches que só crescem\" (...) \"quando múltiplas goroutines leem, escrevem e sobrescrevem entradas para conjuntos disjuntos de chaves (...) Nesses dois casos, o uso de um ",[43,2338,2039],{}," pode reduzir significativamente a contenção de locks em comparação com um map do Go pareado com um ",[43,2341,2342],{},"Mutex",[43,2344,2345],{},"RWMutex"," separado.\"",[33,2348,325,2349],{},[37,2350,1509,2352,608,2354],{"href":2045,"rel":2351},[330],[43,2353,2049],{},[43,2355,2039],{},[33,2357,2358],{},"Medindo exatamente o primeiro cenário — popular o cache uma vez, depois bombardear com leituras concorrentes de 8 goroutines:",[55,2360,2363],{"className":2361,"code":2362,"language":265,"meta":60},[263],"BenchmarkMutexCache-8   23049524    52.67 ns\u002Fop    0 B\u002Fop   0 allocs\u002Fop\nBenchmarkSyncMap-8     185469519     7.193 ns\u002Fop    0 B\u002Fop   0 allocs\u002Fop\n",[43,2364,2362],{"__ignoreMap":60},[33,2366,2367,2369,2370,2372,2373,2375,2376,2379],{},[43,2368,2030],{}," é cerca de 7× mais rápido aqui, e a razão é estrutural. Desde o Go 1.24, ",[43,2371,2030],{}," não é mais implementado com o desenho clássico de \"read map somado a dirty map protegida por ",[43,2374,2342],{},"\": por baixo ele usa uma hash-trie concorrente (",[43,2377,2378],{},"internal\u002Fsync.HashTrieMap","), que a documentação descreve como \"desenhada para leituras frequentes, mas com desempenho razoável também para escritas e remoções\". A release note é direta sobre o ganho:",[315,2381,2382,2388],{},[33,2383,2384,2385,2387],{},"\"A implementação de ",[43,2386,2030],{}," mudou, melhorando a performance, particularmente para modificações no map. Por exemplo, modificações em conjuntos disjuntos de chaves têm muito menos chance de disputa em maps grandes, e não existe mais nenhum tempo de aquecimento necessário para atingir baixa contenção em leituras do map.\"",[33,2389,325,2390],{},[37,2391,363],{"href":361,"rel":2392},[330],[33,2394,2395,2396,2398,2399,2401,2402,2404,2405,2407],{},"Fora do padrão \"escreve uma vez, lê muitas vezes\" (ou do padrão de chaves disjuntas), essa vantagem desaparece ou inverte, porque ",[43,2397,2030],{}," ainda paga um custo de indireção que o ",[43,2400,45],{},"+",[43,2403,2342],{}," não tem. É por isso que a documentação não recomenda ",[43,2406,2030],{}," como padrão: ela ajuda exatamente dois formatos de acesso, e atrapalha fora deles.",[304,2409,2411],{"id":2410},"limitações-exceções-e-armadilhas","Limitações, exceções e armadilhas",[33,2413,2414],{},[283,2415,2416,2417,2419],{},"Você não pode atribuir a um campo de struct dentro de um ",[43,2418,45],{}," diretamente.",[55,2421,2423],{"className":57,"code":2422,"language":59,"meta":60,"style":60},"type Order struct{ Status string }\n\nm := map[string]Order{\"a\": {Status: \"pending\"}}\nm[\"a\"].Status = \"paid\"\n",[43,2424,2425,2444,2448,2492],{"__ignoreMap":60},[64,2426,2427,2429,2432,2434,2436,2439,2441],{"class":66,"line":67},[64,2428,71],{"class":70},[64,2430,2431],{"class":74}," Order",[64,2433,78],{"class":70},[64,2435,731],{"class":81},[64,2437,2438],{"class":88}," Status ",[64,2440,98],{"class":97},[64,2442,2443],{"class":81}," }\n",[64,2445,2446],{"class":66,"line":85},[64,2447,117],{"emptyLinePlaceholder":116},[64,2449,2450,2452,2454,2456,2458,2460,2462,2465,2467,2469,2471,2473,2475,2477,2480,2482,2484,2487,2489],{"class":66,"line":107},[64,2451,1524],{"class":88},[64,2453,513],{"class":70},[64,2455,1043],{"class":70},[64,2457,94],{"class":81},[64,2459,98],{"class":97},[64,2461,101],{"class":81},[64,2463,2464],{"class":74},"Order",[64,2466,731],{"class":81},[64,2468,250],{"class":249},[64,2470,37],{"class":253},[64,2472,250],{"class":249},[64,2474,348],{"class":81},[64,2476,1777],{"class":81},[64,2478,2479],{"class":88},"Status",[64,2481,348],{"class":81},[64,2483,587],{"class":249},[64,2485,2486],{"class":253},"pending",[64,2488,250],{"class":249},[64,2490,2491],{"class":81},"}}\n",[64,2493,2494,2496,2498,2500,2502,2504,2507,2510,2512,2514,2517],{"class":66,"line":113},[64,2495,460],{"class":88},[64,2497,94],{"class":81},[64,2499,250],{"class":249},[64,2501,37],{"class":253},[64,2503,250],{"class":249},[64,2505,2506],{"class":81},"].",[64,2508,2509],{"class":88},"Status ",[64,2511,1115],{"class":70},[64,2513,587],{"class":249},[64,2515,2516],{"class":253},"paid",[64,2518,2519],{"class":249},"\"\n",[55,2521,2524],{"className":2522,"code":2523,"language":265,"meta":60},[263],"cannot assign to struct field m[\"a\"].Status in map\n",[43,2525,2523],{"__ignoreMap":60},[33,2527,2528,2529,2532,2533,2535,2536,2539,2540,2543],{},"O motivo é que ",[43,2530,2531],{},"m[\"a\"]"," não é endereçável: cada acesso de leitura devolve uma cópia do valor guardado, não um ponteiro para dentro do ",[43,2534,45],{},". A saída é ler, alterar a cópia e regravar (",[43,2537,2538],{},"o := m[\"a\"]; o.Status = \"paid\"; m[\"a\"] = o","), ou guardar ",[43,2541,2542],{},"map[string]*Order"," desde o início.",[33,2545,2546,2549,2550,2553,2554,2556,2557,2559,2560,2563,2564,2566,2567,2569,2570,2061,2573,2576,2577,2579],{},[283,2547,2548],{},"Maps não encolhem sozinhos."," Depois de remover 2 milhões de entradas de um ",[43,2551,2552],{},"map[int][32]byte",", seja com ",[43,2555,578],{}," em loop, seja com ",[43,2558,980],{},", o heap continuou no mesmo patamar de antes da remoção (192 MiB, medido com ",[43,2561,2562],{},"runtime.ReadMemStats",", antes e depois). O ",[43,2565,45],{}," mantém a tabela interna no tamanho do pico de uso; ela só é liberada quando o ",[43,2568,45],{}," inteiro deixa de ser referenciado (",[43,2571,2572],{},"m = make(map[K]V)",[43,2574,2575],{},"m = nil",") e o GC recolhe a estrutura antiga. Se um ",[43,2578,45],{}," de vida longa passa por um pico e depois fica pequeno, considerar recriá-lo é uma opção real, não paranoia.",[33,2581,2582,2588,2589,2591,2592,2594],{},[283,2583,2584,2585,2587],{},"A detecção de concorrência é probabilística, o ",[43,2586,1940],{}," não."," Um programa que roda sem ",[43,2590,1963],{}," várias vezes seguidas não está provado correto — a chance de duas goroutines colidirem no exato instante que o runtime verifica depende de timing. CI que quer pegar isso de verdade precisa rodar com ",[43,2593,1940],{},", não só observar ausência de crash.",[33,2596,2597,2602],{},[283,2598,2599,2601],{},[43,2600,49],{}," map e map vazio não são a mesma coisa na borda com JSON",", no mesmo espírito do que já valia para slices:",[55,2604,2606],{"className":57,"code":2605,"language":59,"meta":60,"style":60},"var nilMap map[string]int\nemptyMap := map[string]int{}\n\njson.Marshal(nilMap)   \u002F\u002F null\njson.Marshal(emptyMap) \u002F\u002F {}\n",[43,2607,2608,2625,2645,2649,2667],{"__ignoreMap":60},[64,2609,2610,2612,2615,2617,2619,2621,2623],{"class":66,"line":67},[64,2611,228],{"class":70},[64,2613,2614],{"class":88}," nilMap ",[64,2616,45],{"class":70},[64,2618,94],{"class":81},[64,2620,98],{"class":97},[64,2622,101],{"class":81},[64,2624,104],{"class":97},[64,2626,2627,2630,2632,2634,2636,2638,2640,2642],{"class":66,"line":85},[64,2628,2629],{"class":88},"emptyMap ",[64,2631,513],{"class":70},[64,2633,1043],{"class":70},[64,2635,94],{"class":81},[64,2637,98],{"class":97},[64,2639,101],{"class":81},[64,2641,751],{"class":97},[64,2643,2644],{"class":81},"{}\n",[64,2646,2647],{"class":66,"line":107},[64,2648,117],{"emptyLinePlaceholder":116},[64,2650,2651,2653,2655,2657,2659,2662,2664],{"class":66,"line":113},[64,2652,830],{"class":88},[64,2654,168],{"class":81},[64,2656,1514],{"class":142},[64,2658,146],{"class":81},[64,2660,2661],{"class":88},"nilMap",[64,2663,139],{"class":81},[64,2665,2666],{"class":475},"   \u002F\u002F null\n",[64,2668,2669,2671,2673,2675,2677,2680,2682],{"class":66,"line":120},[64,2670,830],{"class":88},[64,2672,168],{"class":81},[64,2674,1514],{"class":142},[64,2676,146],{"class":81},[64,2678,2679],{"class":88},"emptyMap",[64,2681,139],{"class":81},[64,2683,2684],{"class":475}," \u002F\u002F {}\n",[33,2686,2687,2688,2690,2691,2694,2695,2697],{},"Se a sua API devolve um objeto e o contrato promete ",[43,2689,921],{}," em vez de ",[43,2692,2693],{},"null",", garanta que o ",[43,2696,45],{}," foi inicializado antes de serializar.",[33,2699,2700,2705,2706,2708,2709,2711,2712,2714,2715,2717],{},[283,2701,2702,2704],{},[43,2703,2030],{}," não é gratuito fora do seu nicho."," A própria documentação cita free lists de objetos de vida curta como um exemplo de uso ruim: \"o overhead não se amortiza bem nesse cenário\". Fora dos dois padrões documentados, comece com ",[43,2707,45],{}," + ",[43,2710,2342],{},"\u002F",[43,2713,2345],{}," e só troque se um profile mostrar contenção de lock que ",[43,2716,2030],{}," resolveria.",[304,2719,2721],{"id":2720},"mudanças-entre-versões","Mudanças entre versões",[2723,2724,2725,2738],"table",{},[2726,2727,2728],"thead",{},[2729,2730,2731,2735],"tr",{},[2732,2733,2734],"th",{},"Versão",[2732,2736,2737],{},"O que mudou",[2739,2740,2741,2753,2773,2805,2824],"tbody",{},[2729,2742,2743,2747],{},[2744,2745,2746],"td",{},"Go 1.6",[2744,2748,2749,2750,2752],{},"Detecção leve e best-effort de uso concorrente incorreto de maps, com ",[43,2751,1963],{}," diagnosticando a condição.",[2729,2754,2755,2758],{},[2744,2756,2757],{},"Go 1.21",[2744,2759,2760,2761,2763,2764,2766,2767,2769,2770,2772],{},"Builtin ",[43,2762,1010],{}," (esvazia um map sem realocar e sem torná-lo ",[43,2765,49],{},"); pacotes ",[43,2768,1306],{}," e ",[43,2771,1302],{}," chegam à biblioteca padrão.",[2729,2774,2775,2778],{},[2744,2776,2777],{},"Go 1.23",[2744,2779,2780,2781,2784,2785,608,2788,2769,2791,2794,2795,2784,2798,2769,2801,2804],{},"Iteradores ",[43,2782,2783],{},"range-over-func","; ",[43,2786,2787],{},"maps.Keys",[43,2789,2790],{},"maps.Values",[43,2792,2793],{},"maps.All"," passam a devolver ",[43,2796,2797],{},"iter.Seq",[43,2799,2800],{},"slices.Sorted",[43,2802,2803],{},"slices.Collect"," chegam para consumi-los.",[2729,2806,2807,2809],{},[2744,2808,341],{},[2744,2810,2811,2812,2814,2815,2817,2818,2820,2821,168],{},"Nova implementação de ",[43,2813,45],{}," baseada em Swiss Tables, reversível via ",[43,2816,369],{},". Redução média de 2–3% no overhead de CPU do runtime; nenhuma mudança de semântica. ",[43,2819,2030],{}," também foi reimplementado sobre uma hash-trie concorrente, reversível via ",[43,2822,2823],{},"GOEXPERIMENT=nosynchashtriemap",[2729,2825,2826,2829],{},[2744,2827,2828],{},"Go 1.25+ (experimental)",[2744,2830,2831,2832,2834,2835,2837,2838,2840,2841,2843],{},"Pacote ",[43,2833,1656],{},", atrás de ",[43,2836,1660],{},", inverte o padrão de serialização de maps para ordem não determinística, com a opção ",[43,2839,1664],{}," para restaurar o comportamento de v1. Ainda não é o ",[43,2842,971],{}," padrão.",[304,2845,2847],{"id":2846},"juntando-tudo-o-caminho-mental","Juntando tudo: o caminho mental",[33,2849,2850],{},"Se eu tivesse que resumir o raciocínio em uma sequência de perguntas:",[2852,2853,2854,2879,2895,2913,2922],"ol",{},[2855,2856,2857,2863,2864,395,2866,2868,2869,2871,2872,608,2874,2769,2876,2878],"li",{},[283,2858,2859,2860,2862],{},"Esse ",[43,2861,45],{}," foi inicializado antes da primeira escrita?"," Zero value de ",[43,2865,45],{},[43,2867,49],{},", e ",[43,2870,49],{}," aceita leitura, ",[43,2873,489],{},[43,2875,611],{},[43,2877,578],{}," — só não aceita escrita. Se o campo vem de um struct literal parcial ou de um zero value implícito, confira.",[2855,2880,2881,2887,2888,2891,2892,2894],{},[283,2882,2883,2884,2886],{},"Meu código depende da ordem em que o ",[43,2885,611],{}," devolve os elementos?"," Se depende, isso é um bug latente, não uma coincidência que ainda não apareceu. Ordene as chaves explicitamente (",[43,2889,2890],{},"slices.Sorted(maps.Keys(m))",") ou deixe o ",[43,2893,971],{}," fazer isso por você na borda de serialização.",[2855,2896,2897,2902,2903,2708,2905,2711,2907,2909,2910,2912],{},[283,2898,2859,2899,2901],{},[43,2900,45],{}," é acessado por mais de uma goroutine?"," Se só há leitura, nenhuma sincronização é necessária. No instante em que existe uma escrita concorrente, escolha entre ",[43,2904,45],{},[43,2906,2342],{},[43,2908,2345],{}," (o caso geral) ou ",[43,2911,2030],{}," (escrita única com muitas leituras, ou chaves disjuntas por goroutine).",[2855,2914,2915,2921],{},[283,2916,2917,2918,2920],{},"Meus testes de concorrência rodam com ",[43,2919,1940],{},"?"," Se não rodam, \"nunca vi crashar\" não significa \"está correto\".",[2855,2923,2924,2929],{},[283,2925,2859,2926,2928],{},[43,2927,45],{}," já teve um pico de tamanho e hoje está pequeno?"," Se teve, ele ainda está segurando a memória do pico. Recriar é uma opção legítima.",[33,2931,2932,2933,168],{},"O fio que conecta os três assuntos deste artigo é o mesmo que já apareceu nos anteriores: Go tenta te dar previsibilidade de graça (zero value determinístico, verificação de corrida embutida no runtime) e, no ponto exato em que a previsibilidade custaria caro demais para o caso comum, ela para. A ordem de iteração podia ser estável e não é, de propósito, porque estabilidade acidental vira dependência de manutenção. A escrita concorrente podia ser silenciosamente permitida e não é, porque silenciosa é pior que crash. Entender onde essa linha foi traçada é entender o ",[43,2934,45],{},[304,2936,2938],{"id":2937},"referências","Referências",[2940,2941,2942,2952,2961,2968,2975,2994,3013,3019,3034,3046,3064,3080,3094,3107],"ul",{},[2855,2943,2944,2948,2949,2951],{},[37,2945,2947],{"href":328,"rel":2946},[330],"The Go Programming Language Specification — Map types"," — definição de map, zero value ",[43,2950,49],{}," e a exceção de \"nenhum elemento pode ser adicionado\".",[2855,2953,2954,2958,2959,168],{},[37,2955,2957],{"href":1171,"rel":2956},[330],"The Go Programming Language Specification — For statements"," — o texto exato sobre ordem de iteração não especificada e comportamento com inserção\u002Fremoção durante o ",[43,2960,611],{},[2855,2962,2963,2967],{},[37,2964,2966],{"href":1181,"rel":2965},[330],"Go Blog — Go maps in action"," — confirmação, desde 2013, de que a ordem de iteração já era documentada como não garantida.",[2855,2969,2970,2974],{},[37,2971,2973],{"href":1686,"rel":2972},[330],"Go FAQ — Why are map operations not defined to be atomic?"," — a posição oficial sobre segurança de leitura concorrente e a recomendação de sincronização para escrita.",[2855,2976,2977,2982,2983,2711,2986,2989,2990,2993],{},[37,2978,2980],{"href":1278,"rel":2979},[330],[43,2981,1282],{}," — o iterador do Swiss Table map, os campos ",[43,2984,2985],{},"entryOffset",[43,2987,2988],{},"dirOffset"," e a chamada a ",[43,2991,2992],{},"rand()"," na inicialização.",[2855,2995,2996,3002,3003,3005,3006,3009,3010,168],{},[37,2997,3000],{"href":2998,"rel":2999},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Finternal\u002Fruntime\u002Fmaps\u002Fmap.go",[330],[43,3001,2000],{}," — o campo ",[43,3004,1996],{}," e as chamadas a ",[43,3007,3008],{},"fatal(\"concurrent map writes\")"," \u002F ",[43,3011,3012],{},"fatal(\"concurrent map read and map write\")",[2855,3014,3015,3018],{},[37,3016,1990],{"href":1988,"rel":3017},[330]," — a introdução da detecção best-effort de uso concorrente incorreto de maps.",[2855,3020,3021,3026,3027,3029,3030,2769,3032,168],{},[37,3022,3025],{"href":3023,"rel":3024},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Fgo1.21",[330],"Go 1.21 Release Notes"," — o builtin ",[43,3028,1010],{}," e a chegada dos pacotes ",[43,3031,1306],{},[43,3033,1302],{},[2855,3035,3036,3041,3042,2711,3044,168],{},[37,3037,3040],{"href":3038,"rel":3039},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Fgo1.23",[330],"Go 1.23 Release Notes"," — range-over-func e as funções baseadas em iteradores em ",[43,3043,1302],{},[43,3045,1306],{},[2855,3047,3048,3051,3052,3054,3055,3057,3058,3060,3061,3063],{},[37,3049,363],{"href":361,"rel":3050},[330]," — a nova implementação de ",[43,3053,45],{}," baseada em Swiss Tables (",[43,3056,369],{},") e a reimplementação de ",[43,3059,2030],{}," sobre uma hash-trie concorrente (",[43,3062,2823],{},").",[2855,3065,3066,3073,3074,3077,3078,168],{},[37,3067,3070],{"href":3068,"rel":3069},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Finternal\u002Fsync\u002Fhashtriemap.go",[330],[43,3071,3072],{},"internal\u002Fsync\u002Fhashtriemap.go"," — a implementação de ",[43,3075,3076],{},"HashTrieMap"," que hoje serve de base para ",[43,3079,2030],{},[2855,3081,3082,3090,3091,3093],{},[37,3083,1509,3085,3087,3088],{"href":2045,"rel":3084},[330],[43,3086,2049],{}," — ",[43,3089,2039],{}," — os dois casos de uso recomendados para ",[43,3092,2030],{}," e a recomendação padrão de usar map comum com locking.",[2855,3095,3096,3103,3104,3106],{},[37,3097,1509,3099,3087,3101],{"href":1507,"rel":3098},[330],[43,3100,971],{},[43,3102,1514],{}," — a ordenação das chaves de map na serialização e a diferença de comportamento em relação ao ",[43,3105,1656],{}," experimental.",[2855,3108,3109,3113,3114,3116],{},[37,3110,3112],{"href":2012,"rel":3111},[330],"Go Blog — Race Detector"," — a ferramenta correta para detectar corridas de forma confiável, em vez de depender do ",[43,3115,1963],{}," best-effort.",[3118,3119,3120],"style",{},"html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .swQdS, html code.shiki .swQdS{--shiki-light:#E53935;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":60,"searchDepth":85,"depth":85,"links":3122},[3123,3124,3125,3128,3131,3132,3133,3134],{"id":306,"depth":85,"text":307},{"id":388,"depth":85,"text":389},{"id":1017,"depth":85,"text":1018,"children":3126},[3127],{"id":1292,"depth":107,"text":1293},{"id":1668,"depth":85,"text":1669,"children":3129},[3130],{"id":2023,"depth":107,"text":2024},{"id":2410,"depth":85,"text":2411},{"id":2720,"depth":85,"text":2721},{"id":2846,"depth":85,"text":2847},{"id":2937,"depth":85,"text":2938},"2026-08-05","Como o zero value de um map se comporta em Go, por que a ordem de iteração é aleatória de propósito, e como sincronizar acesso concorrente sem escolher a ferramenta errada.","md","https:\u002F\u002Fencrypted-tbn0.gstatic.com\u002Fimages?q=tbn:ANd9GcQvXqKsNKgbxpINxN06GeyY8UPggpH6RUlMZtcJwYptYOPdaD3k4-nHOzd5&s=10",{},{"title":22,"description":3136},"SUXy-AV2TATDXsbF5EP9ZH2VOv7PofjFEfZwEyLMIUk",1786023409654]