├── README.md ├── gotcha-append └── main.go ├── gotcha-assignment-entry-nil-map └── main.go ├── gotcha-concatenate-rune-string └── main.go ├── gotcha-constant-overflows-int └── main.go ├── gotcha-copy-missing └── main.go ├── gotcha-function-doesnt-change-array └── main.go ├── gotcha-increment-decrement-statement └── main.go ├── gotcha-missing-comma-slice-array-map-literal └── main.go ├── gotcha-multiple-value-single-value-context └── main.go ├── gotcha-nil-pointer-dereference └── main.go ├── gotcha-shadowing-variables └── main.go ├── gotcha-strings-are-immutable └── main.go └── gotcha-trim-string └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # Go Gotchas 2 | 3 | [Go Gotchas](https://yourbasic.org/golang/gotcha/) é uma série de 27 desafios na 4 | linguagem Go, propostos para melhoria do código que escrevemos no dia a dia. 5 | 6 | ## Lista de Todos os Desafios 7 | 8 | 1. :white_check_mark: [Assignment to entry in nil map](https://yourbasic.org/golang/gotcha-assignment-entry-nil-map/) 9 | 2. :white_check_mark: [Invalid memory address or nil pointer dereference](https://yourbasic.org/golang/gotcha-nil-pointer-dereference/) 10 | 3. :white_check_mark: [Multiple-value in single-value context](https://yourbasic.org/golang/gotcha-multiple-value-single-value-context/) 11 | 4. :white_check_mark: [Array won't change](https://yourbasic.org/golang/gotcha-function-doesnt-change-array/) 12 | 5. :white_check_mark: [Shadowed variables](https://yourbasic.org/golang/gotcha-shadowing-variables/) 13 | 6. :white_check_mark: [Unexpected newline](https://yourbasic.org/golang/gotcha-missing-comma-slice-array-map-literal/) 14 | 7. :white_check_mark: [Immutable strings](https://yourbasic.org/golang/gotcha-strings-are-immutable/) 15 | 8. :white_check_mark: [How does characters add up?](https://yourbasic.org/golang/gotcha-concatenate-rune-string/) 16 | 9. :white_check_mark: [What happened to ABBA?](https://yourbasic.org/golang/gotcha-trim-string/) 17 | 10. :white_check_mark: [Where is my copy?](https://yourbasic.org/golang/gotcha-copy-missing/) 18 | 11. :white_check_mark: [Why doesn't append work every time? (scary bug)](https://yourbasic.org/golang/gotcha-append/) 19 | 12. :white_check_mark: [Constant overflows int](https://yourbasic.org/golang/gotcha-constant-overflows-int/) 20 | 13. :white_check_mark: [Unexpected ++, expecting expression](https://yourbasic.org/golang/gotcha-increment-decrement-statement/) 21 | 14. [Get your priorities right](https://yourbasic.org/golang/gotcha-operator-precedence/) 22 | 15. [Go and Pythagoras](https://yourbasic.org/golang/gotcha-bitwise-operators/) 23 | 16. [No end in sight](https://yourbasic.org/golang/gotcha-integer-overflow-wrap-around/) 24 | 17. [Numbers that starts with zero](https://yourbasic.org/golang/gotcha-octal-decimal-hexadecimal-literal/) 25 | 18. [Whatever remains](https://yourbasic.org/golang/gotcha-remainder-modulo-operator/) 26 | 19. [Time is not a number](https://yourbasic.org/golang/gotcha-multiply-duration-integer/) 27 | 20. [Index out of range](https://yourbasic.org/golang/gotcha-index-out-of-range/) 28 | 21. [Unexpected values in range loop](https://yourbasic.org/golang/gotcha-unexpected-values-range/) 29 | 22. [Can't change entries in range loop](https://yourbasic.org/golang/gotcha-change-value-range/) 30 | 23. [Iteration variable doesn't see change in range loop](https://yourbasic.org/golang/gotcha-range-copy-array/) 31 | 24. [Iteration variables and closures](https://yourbasic.org/golang/gotcha-data-race-closure/) 32 | 25. [No JSON in sight](https://yourbasic.org/golang/gotcha-json-marshal-empty/) 33 | 26. [Is "three" a digit?](https://yourbasic.org/golang/gotcha-regexp-substring/) 34 | 27. [Nil is not nil](https://yourbasic.org/golang/gotcha-why-nil-error-not-equal-nil/) 35 | 36 | A solução que fiz para cada desafio está dentro de uma pasta neste repositório. 37 | 38 | ## Observações 39 | 40 | Alguns dos desafios propostos foram alterados, corrigidos e/ou modificados desde 41 | a criação dessa lista. Sempre que possível, procuro o 42 | [autor do texto](https://twitter.com/yourbasic_org) para apontar quando encontro 43 | isso, e com qual versão de Go isso pode ter sido corrigido. Segue abaixo uma 44 | lista: 45 | 46 | -------------------------------------------------------------------------------- /gotcha-append/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | /* 9 | O texto original aponta que o trecho de código abaixo resulta em um bug 10 | bizarro: 11 | 12 | a := []byte("ba") 13 | 14 | a1 := append(a, 'd') 15 | a2 := append(a, 'g') 16 | 17 | fmt.Println(string(a1)) // bag 18 | fmt.Println(string(a2)) // bag 19 | 20 | Ambos os slices escrevem a mesma saída, devido a um bug na forma como a 21 | função append funciona (ou melhor, funcionava), onde a, a1 e a2 se 22 | referiam ao mesmo array. Logo, ao editar um desses slices, todos seriam 23 | editados. 24 | 25 | Testei aqui a execução do mesmo código com Docker, em imagens com Linux 26 | Alpine como base, da versão 1.5 até 1.17, e não consegui reproduzir esse 27 | bug a partir da versão 1.12. Estranhamente, a saída correta obtida a 28 | partir de 1.12 foi reproduzida na versão 1.6. Curioso, hehehe. 29 | 30 | A solução apontada para o problema do texto original é usar um array 31 | diferente para cada ação de append(): 32 | */ 33 | const prefix = "ba" 34 | 35 | a1 := append([]byte(prefix), 'd') 36 | a2 := append([]byte(prefix), 'g') 37 | 38 | fmt.Println(string(a1)) // bad 39 | fmt.Println(string(a2)) // bag 40 | 41 | // Assim como no outro caso, também escrevi sobre este no Twitter 42 | // marcando o autor do blog. 43 | 44 | /* 45 | Referências de leitura: 46 | 47 | - https://yourbasic.org/golang/append-explained/ 48 | - https://twitter.com/devdrops/status/1463529279602540544 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /gotcha-assignment-entry-nil-map/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | /* 7 | A declaração abaixo resulta em panic: 8 | ``` 9 | panic: assignment to entry in nil map 10 | ``` 11 | var m map[string]float64 12 | m["pi"] = 3.1416 13 | 14 | Por quê? 15 | Tem a variável criada na declaração `var`, tem a atribuição de valor. 16 | O que falta aqui é a inicialização do map. 17 | 18 | Todo map precisa ser inicializado, seja com a função make ou um map 19 | literal antes de inserir elementos. Todo map tem nil por padrão até ser 20 | inicializado, e enquanto for assim, elementos não podem ser inseridos 21 | nele. 22 | */ 23 | 24 | // map inicializado com uso de make: 25 | m := make(map[string]float64) 26 | m["pi"] = 3.1416 27 | 28 | // map inicializado de forma literal: 29 | var om = map[string]float64{ 30 | "pi": 3.1416, 31 | "aaa": 1.2345, 32 | } 33 | fmt.Println(om) 34 | 35 | /* 36 | Referência de leitura: 37 | 38 | - https://yourbasic.org/golang/maps-explained/ 39 | */ 40 | } 41 | -------------------------------------------------------------------------------- /gotcha-concatenate-rune-string/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | /* 9 | O texto explica que o trecho de código abaixo tem resultados diferentes: 10 | 11 | fmt.Println("H" + "i") // Hi 12 | fmt.Println('H' + 'i') // 177 13 | 14 | A resposta está na diferença entre os tipos. Na primeira linha, temos a 15 | concatenação de dois valores do tipo untyped constant, com pseudo tipo 16 | string. Na segunda linha, temos a soma de dois valores também do tipo 17 | untyped constant, porém com pseudo tipo rune, que ali representam 18 | valores de Unicode ('H' é 72, e 'i' é 105), logo a operação não é uma 19 | concatenação mas sim uma adição, com o resultado abaixo. 20 | 21 | Uma das formas de resolver isso é converter os valores para string, como 22 | o código abaixo: 23 | */ 24 | fmt.Println("H" + "i") // Hi 25 | fmt.Println(string('H') + string('i')) // Hi 26 | fmt.Println(string(72) + string('i')) // Hi 27 | fmt.Println(string('H') + string(105)) // Hi 28 | fmt.Println(string(72) + string(105)) // Hi 29 | /* 30 | Também podemos usar a função fmt.Printf() (a opção %c escreve o 31 | caractere representado pelo valor Unicode): 32 | */ 33 | fmt.Printf("%c%c, world!\n", 'H', 'i') // Hi, world! 34 | fmt.Printf("%c%c, world!\n", 72, 'i') // Hi, world! 35 | fmt.Printf("%c%c, world!\n", 'H', 105) // Hi, world! 36 | fmt.Printf("%c%c, world!\n", 72, 105) // Hi, world! 37 | /* 38 | Referências de leitura: 39 | 40 | - https://pkg.go.dev/fmt#hdr-Printing 41 | - https://yourbasic.org/golang/fmt-printf-reference-cheat-sheet/ 42 | */ 43 | } 44 | -------------------------------------------------------------------------------- /gotcha-constant-overflows-int/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | ) 7 | 8 | func main() { 9 | /* 10 | O código abaixo gera o seguinte erro: 11 | 12 | const n = 9876543210 * 9876543210 13 | fmt.Println(n) 14 | 15 | constant 97546105778997104100 overflows int 16 | 17 | O que houve? 18 | 19 | Este cálculo é simples, mas há um detalhe. A constante n é declarada 20 | sem o tipo de forma explícita, logo o tipo é inferido. assumindo int. 21 | Porém, a conversão é necessária antes da chamada à fmt.Println() para 22 | que a conversão para o tipo esperado (interface{}) funcione 23 | corretamente. No final, o resultado da multiplicação excede o tamanho 24 | máximo do tipo int, o que causa o problema. 25 | 26 | Isso pode ser resolvido aplicando um tipo à constante, como float64 por 27 | exemplo. No entanto, é válido aqui explorar o conteúdo do package 28 | math/big para operar melhor com esse tipo de valores. 29 | */ 30 | 31 | v1 := big.NewInt(9876543210) 32 | v2 := big.NewInt(9876543210) 33 | on := big.NewInt(0) 34 | fmt.Println(on.Mul(v1, v2)) // 97546105778997104100 35 | /* 36 | Referências de leitura: 37 | 38 | - https://pkg.go.dev/math/big 39 | */ 40 | } 41 | -------------------------------------------------------------------------------- /gotcha-copy-missing/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | /* 9 | O trecho de código abaixo tem um problema: queremos copiar os elementos 10 | de src para dst, porém, quando o fazemos e escrevemos o conteúdo de dst, 11 | o slice aparece como vazio, sem os elementos copiados. 12 | 13 | var src, dst []int 14 | src = []int{1, 2, 3} 15 | copy(dst, src) 16 | fmt.Println("dst: ", dst) 17 | 18 | O que houve aqui? 19 | A função copy exige que a variável de destino tenha no mínimo o mesmo 20 | tamanho da variável de origem. Como a variável dst foi apenas criada, 21 | sem elementos inseridos nem tamanho definido, a operação de cópia acaba 22 | não acontecendo. 23 | 24 | Para corrigir isso, podemos usar a função make para "inicializar" a 25 | variável dst com a mesma quantidade de elementos que src tem: 26 | */ 27 | var src, dst []int 28 | src = []int{1, 2, 3} 29 | dst = make([]int, len(src)) 30 | copy(dst, src) 31 | fmt.Println("dst: ", dst) 32 | fmt.Println("cap dst: ", cap(dst)) 33 | /* 34 | Podemos também usar a função append para chegar no mesmo resultado. A 35 | diferença aqui é que append pode criar um slice com a capacidade maior 36 | de elementos do que len(osrc). 37 | */ 38 | var osrc, odst []int 39 | osrc = []int{1, 2, 3} 40 | odst = append(odst, osrc...) 41 | fmt.Println("odst: ", odst) 42 | fmt.Println("cap odst: ", cap(odst)) 43 | /* 44 | Referências de leitura: 45 | 46 | - https://yourbasic.org/golang/copy-explained/ 47 | - https://pkg.go.dev/builtin#append 48 | - https://pkg.go.dev/builtin#copy 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /gotcha-function-doesnt-change-array/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Foo(a [2]int) { 8 | a[0] = 8 9 | } 10 | 11 | func OtoFoo(a []int) { 12 | if len(a) > 0 { 13 | a[0] = 8 14 | } 15 | } 16 | 17 | func main() { 18 | /* 19 | Este aqui tem um detalhe muito interessante. 20 | 21 | A princípio, olhei para o código e pensei "pô, mas não vai atualizar de 22 | jeito nenhum assim porque estamos passando uma cópia e não o mesmo 23 | array. E de fato é isso o motivo do array abaixo não ser atualizado: 24 | */ 25 | 26 | a := [2]int{1, 2} 27 | Foo(a) 28 | fmt.Println(a) 29 | 30 | /* 31 | Numa explicação mais detalhada: a função Foo espera um array, e, ao 32 | chamá-la, passo a variável a. Só que, por questões de escopo (que são 33 | comuns em muitas linguagens de programação), a operação aqui trabalha 34 | com os dados dentro do seu escopo. Ou seja, dentro de Foo, só se sabe o 35 | que está dentro de Foo e ponto final, assim como dentro de main etc. 36 | Logo, da forma como o código foi escrito, o array nunca será atualizado 37 | pela função Foo. 38 | 39 | Podemos resolver isso passando um ponteiro do array para a função Foo, 40 | por exemplo. Aí sim, o escopo de Foo passa a receber um valor de fora, 41 | e, ao fazer sua operação, temos de fato a atualização do array. 42 | 43 | func Foo(a *[2]int) { 44 | a[0] = 8 45 | } 46 | 47 | a := [2]int{1, 2} 48 | Foo(&a) 49 | fmt.Println(a) // [8 2] 50 | 51 | MAS, uma coisa que o texto diz e que eu não sabia, é usar um slice ao 52 | invés de um array. 53 | */ 54 | 55 | s := []int{1, 2} 56 | OtoFoo(s) 57 | fmt.Println(s) // [8 2] 58 | 59 | /* 60 | Mas, POR QUÊ? 61 | 62 | Segundo o texto original, um slice não armazena dados, apenas descreve 63 | uma parte de um array. Isso quer dizer que ao passarmos um slice para 64 | uma função, estamos na verdade passando a referência do segmento de um 65 | array? 66 | 67 | Parece que sim, e isso eu não sabia. Ao mudar o valor de um elemento num 68 | slice, estamos mudando o valor do array, e outros slices deste mesmo 69 | array vão ter a mesma atualização. Malandro, que pegadinha sinistra! 70 | */ 71 | 72 | /* 73 | Referências de leitura: 74 | 75 | - https://yourbasic.org/golang/slices-explained/ 76 | */ 77 | } 78 | -------------------------------------------------------------------------------- /gotcha-increment-decrement-statement/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | /* 9 | O trecho de código abaixo gera dois erros de sintaxe: 10 | 11 | i := 0 12 | fmt.Println(++i) 13 | fmt.Println(i++) 14 | 15 | syntax error: unexpected ++, expecting expression 16 | syntax error: unexpected ++, expecting comma or ) 17 | 18 | Por quê? 19 | 20 | Em Go, as operações de incremento e decremento não podem ser usadas 21 | como expressões (algo passado para uma função), apenas como declarações. 22 | E também, só funcionam se for pós (o uso como pré também cai no mesmo 23 | erro de sintaxe), como no trecho de código abaixo: 24 | 25 | i := 0 26 | ++i 27 | fmt.Println(i) 28 | 29 | syntax error: unexpected ++, expecting } 30 | 31 | A forma de resolver isso é fazer cada etapa de uma vez: primeiro a 32 | operação (incremento ou decremento), depois a próxima tarefa: 33 | */ 34 | i := 0 35 | i++ 36 | fmt.Println(i) // 1 37 | i-- 38 | fmt.Println(i) // 0 39 | /* 40 | Referências de leitura: 41 | 42 | - https://go.dev/doc/faq#inc_dec 43 | */ 44 | } 45 | -------------------------------------------------------------------------------- /gotcha-missing-comma-slice-array-map-literal/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | /* 9 | O código abaixo vai gerar um erro de sintaxe: 10 | 11 | ``` 12 | fruit := []string{ 13 | "apple", 14 | "banana", 15 | "cherry" 16 | } 17 | fmt.Println(fruit) 18 | ``` 19 | 20 | ``` 21 | syntax error: unexpected newline, expecting comma or } 22 | ``` 23 | 24 | A explicação, para quem não conhece: em slices, arrays e maps onde 25 | escrevemos seus elementos em mais de uma linha, cada linha de um 26 | elemento deve terminar com uma vírgula, como o código abaixo: 27 | */ 28 | 29 | fruit := []string{ 30 | "apple", 31 | "banana", 32 | "cherry", // olha a vírgula aqui no final 33 | } 34 | fmt.Println(fruit) 35 | 36 | /* 37 | Referências de leitura: 38 | 39 | - https://go.dev/ref/spec#Semicolons 40 | */ 41 | } 42 | -------------------------------------------------------------------------------- /gotcha-multiple-value-single-value-context/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | /* 10 | Aqui temos um detalhe. 11 | 12 | O texto original deste desafio explica que o código abaixo resulta no 13 | seguinte erro de compilação: 14 | 15 | ``` 16 | multiple-value time.Parse() in single-value context 17 | ``` 18 | 19 | Este é o código proposto: 20 | */ 21 | 22 | t := time.Parse(time.RFC3339, "2018-04-06T10:49:05Z") 23 | fmt.Println(t) 24 | 25 | /* 26 | O texto segue explicando um detalhe comum, a confusão de tentar atribuir 27 | a somente uma variável o retorno de dois valores distintos. O método 28 | time.Parse() retorna um Time e um error. Logo, os caminhos aqui podem 29 | ser os seguintes: 30 | 31 | // Analisando o retorno de erro 32 | t, err := time.Parse(time.RFC3339, "2018-04-06T10:49:05Z") 33 | if err != nil { 34 | fmt.Println(err.Error()) 35 | } 36 | fmt.Println(t) 37 | 38 | // Ignorando o retorno de erro com _ (underscore) 39 | t, _ := time.Parse(time.RFC3339, "2018-04-06T10:49:05Z") 40 | fmt.Println(t) 41 | 42 | O que chamou a atenção é que a mensagem que obtive foi diferente da que 43 | é descrita no texto. Aqui, ao executar o código conforme o texto 44 | original, recebi a seguinte mensagem: 45 | 46 | ``` 47 | assignment mismatch: 1 variable but time.Parse returns 2 values 48 | ``` 49 | 50 | A versão de Go que usei: go version go1.17.2 linux/amd64. 51 | 52 | Ou seja, acredito que a particularidade aqui é que a mensagem era fora 53 | do que conhecemos, um pouco estranha mas que podemos entender. O 54 | importante é notar que essa mensagem não existe mais (até avisei o 55 | autor dos desafios no Twitter, para ver se podemos atualizar a lista de 56 | desafios). 57 | 58 | */ 59 | 60 | /* 61 | Referências de leitura: 62 | 63 | - https://yourbasic.org/golang/underscore/ 64 | - https://pkg.go.dev/time#Parse 65 | - https://twitter.com/devdrops/status/1461713172562382848 66 | */ 67 | } 68 | -------------------------------------------------------------------------------- /gotcha-nil-pointer-dereference/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type Point struct { 9 | X, Y float64 10 | } 11 | 12 | func (p *Point) Abs() float64 { 13 | return math.Sqrt(p.X * p.X + p.Y * p.Y) 14 | } 15 | 16 | func main() { 17 | /* 18 | O código abaixo causa panic: 19 | ``` 20 | panic: runtime error: invalid memory address or nil pointer dereference 21 | ``` 22 | var p *Point 23 | fmt.Println(p.Abs()) 24 | 25 | Por quê? 26 | 27 | O ponteiro p está declarado mas não está inicializado, logo é nil. Daí o 28 | panic. 29 | 30 | Para corrigir isso, basta inicializar criando algo do tipo Point, usando 31 | a função new() por exemplo, que faz a alocação de memória retornando o 32 | ponteiro (valor do endereço de memória) do tipo então criado. 33 | */ 34 | // Criando através da função new() 35 | var p *Point = new(Point) 36 | fmt.Println(p.Abs()) 37 | 38 | /* 39 | Pode-se também contornar o problema se criarmos a variável literal, ao 40 | invés de um ponteiro: 41 | */ 42 | // Criando sem usar um ponteiro 43 | var op Point 44 | fmt.Println(op.Abs()) 45 | 46 | /* 47 | Referências de leitura: 48 | 49 | - https://pkg.go.dev/builtin#new 50 | - https://yourbasic.org/golang/pointers-explained/ 51 | */ 52 | } 53 | -------------------------------------------------------------------------------- /gotcha-shadowing-variables/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | /* 9 | Esse é bem interessante. 10 | 11 | O código abaixo resulta em n mantendo o valor designado 0 (zero). A 12 | operação feita dentro do if não resulta em alteração do valor de n. 13 | 14 | ``` 15 | n := 0 16 | if true { 17 | n := 1 18 | n++ 19 | } 20 | fmt.Println(n) 21 | ``` 22 | 23 | Por quê? 24 | 25 | A declaração n := 1 faz a criação de uma nova variável n dentro do 26 | escopo do if, que acaba "obscurendo" (shadowing) a variável criada fora 27 | do if. 28 | 29 | Se queremos usar dentro do if a mesma variável n, devemos mudar o código 30 | para o seguinte: 31 | 32 | ``` 33 | n := 0 34 | if true { 35 | n = 1 36 | n++ 37 | } 38 | fmt.Println(n) 39 | ``` 40 | 41 | Com o uso de =, estamos usando a mesma exata variável fora do if. 42 | 43 | E ainda: como podemos detectar a situação de variáveis "obscurecidas"? 44 | Podemos usar `go vet` para isso, da forma abaixo: 45 | 46 | Primeiro, instalamos a ferramenta necessária para a análise: 47 | ``` 48 | go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest 49 | ``` 50 | (a versão da ferramenta é exigida na instalação) 51 | 52 | 53 | Depois, usamos o comando go vet com a ferramenta instalada: 54 | ``` 55 | go vet -vettool=$(which shadow) main.go 56 | ``` 57 | 58 | Com isso, teremos a seguinte análise: 59 | ``` 60 | ./main.go:66:3: declaration of "n" shadows declaration at line 63 61 | ``` 62 | */ 63 | n := 0 64 | if true { 65 | n := 1 66 | n++ 67 | } 68 | fmt.Println(n) 69 | 70 | /* 71 | Referências de leitura: 72 | 73 | - go help vet 74 | - go doc cmd/vet 75 | - https://pkg.go.dev/cmd/go/internal/vet 76 | */ 77 | } 78 | -------------------------------------------------------------------------------- /gotcha-strings-are-immutable/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | /* 9 | O código abaixo resulta em um erro: 10 | 11 | ``` 12 | s := "hello" 13 | s[0] = "H" 14 | fmt.Println(s) 15 | ``` 16 | 17 | ``` 18 | cannot assign to s[0] (strings are immutable) 19 | ``` 20 | 21 | A mensagem é direta: strings são imutáveis, ou seja, não podemos mudar 22 | seus valores da forma como está ali no código. 23 | 24 | Para mudar o valor de s, devemos usar outro tipo de dados, rune, como o 25 | exemplo abaixo: 26 | */ 27 | buf := []rune("hello") 28 | /* 29 | Temos que usar aspas simples para delimitar a string aqui; do contrário, 30 | teremos outro erro, `cannot use "H" (type untyped string) as type rune 31 | in assignment`, dado que a string delimitada por aspas duplas é 32 | considerada uma constante não-tipada (untyped constant), e seu tipo não 33 | é o mesmo que usamos em buf. 34 | 35 | Vale lembrar que constantes não-tipadas possuem sim um tipo definido, 36 | como todo valor em Go. A diferença é que seu tipo é oculto, não sendo o 37 | mesmo que rune. Daí o problema :) 38 | */ 39 | buf[0] = 'H' 40 | s := string(buf) 41 | fmt.Println(s) 42 | 43 | /* 44 | Referências de leitura: 45 | 46 | - https://yourbasic.org/golang/string-functions-reference-cheat-sheet/ 47 | - https://pkg.go.dev/builtin#string 48 | - https://pkg.go.dev/builtin#rune 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /gotcha-trim-string/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func main() { 9 | /* 10 | O trecho de código abaixo tem um resultado "inesperado": 11 | 12 | fmt.Println(strings.TrimRight("ABBA", "BA")) // "" 13 | 14 | É isso aí, uma string vazia. Por quê? 15 | As funções Trim, TrimLeft e TrimRight removem todos os caracteres cujos 16 | valores de Unicode se encontram no trecho dado. No exemplo dado, todos 17 | os A:s e B:s são afetados, deixando apenas a string vazia no final. 18 | 19 | Para resolver isso, podemos usar TrimSuffix (que age no sufixo da 20 | string): 21 | */ 22 | fmt.Println(strings.TrimSuffix("ABBA", "BA")) // "AB" 23 | /* 24 | Referências de leitura: 25 | 26 | - https://pkg.go.dev/strings#Trim 27 | - https://pkg.go.dev/strings#TrimLeft 28 | - https://pkg.go.dev/strings#TrimRight 29 | - https://pkg.go.dev/strings#TrimSuffix 30 | - https://yourbasic.org/golang/string-functions-reference-cheat-sheet/ 31 | */ 32 | } 33 | --------------------------------------------------------------------------------