# 2.2 Fundamentos em Go Nesta seção vamos aprender como definir constantes, variáveis com tipos elementares e algumas habilidades de programação em Go. ## Definir variáveis Existem diversas formas de sintaxe que podem ser utilizadas para definir variáveis em Go. A palavra-chave `var` é a forma mais básica de definir variáveis. Observe que a linguagem Go coloca o tipo da variável `depois` do nome da variável. // define uma variável com o nome “variableName” e o tipo "type" var variableName type Definir múltiplas variáveis. // define três variáveis do tipo "type" var vname1, vname2, vname3 type Definir uma variável com um valor inicial. // define uma variável com o nome “variableName”, tipo "type" e valor "value" var variableName type = value Definir múltiplas variáveis com valores iniciais. /* Define três variáveis de tipo "type" e inicializa seus valores. vname1 recebe v1, vname2 recebe v2, vname3 recebe v3 */ var vname1, vname2, vname3 type = v1, v2, v3 Você acha muito tedioso definir variáveis desta maneira? Não se preocupe porque a equipe da Go também achou que isto poderia ser um problema. Portanto, se você deseja definir variáveis com valores iniciais, pode simplesmente omitir o tipo da variável. Sendo assim, o código ficará da seguinte forma: /* Define três variáveis sem o tipo "type" e inicializa seus valores. vname1 recebe v1, vname2 recebe v2, vname3 recebe v3 */ var vname1, vname2, vname3 = v1, v2, v3 Bem, talvez isto ainda não seja simples o suficiente para você. Vamos ver como podemos melhorar. /* Define três variáveis sem o tipo "type", sem a palavra-chave "var" e inicializa seus valores. vname1 recebe v1, vname2 recebe v2, vname3 recebe v3 */ vname1, vname2, vname3 := v1, v2, v3 Agora parece muito melhor. Use `:=` para substituir `var` e `type`. Isto é chamado de uma "declaração curta" (do inglês: brief statement). Mas espere, isto possui uma limitação: esta forma só pode ser utilizada dentro de funções. Você receberá erros de compilação se tentar utilizar isto fora do corpo de uma função. Portanto, normalmente utilizamos `var` para definir variáveis globais e podemos utilizar esta declaração curta em `var()`. `_` (blank) é um nome especial de variável. Qualquer valor que seja atribuído a isto será ignorado. Por exemplo, vamos atribuir o valor `35` a variável `b` e descartar o valor `34`.( ***Este exemplo apenas mostra como isto funciona. Ele parece inútil aqui porque frequentemente utilizamos este símbolo quando recebemos valores de retorno de uma função. *** ) _, b := 34, 35 Se você não utilizar variáveis que foram definidas no seu programa, o compilador irá gerar erros de compilação. Tente compilar o seguinte código e veja o que acontece. package main func main() { var i int } ## Constantes Constantes são os valores que são determinados durante o tempo de compilação e você não pode alterá-los durante a execução. Em Go, você pode utilizar número, booleano ou string como tipos de constantes. Defina as constantes da seguinte forma. const constantName = value // você pode atribuir o tipo da constante se for necessário const Pi float32 = 3.1415926 Mais exemplos. const Pi = 3.1415926 const i = 10000 const MaxThread = 10 const prefix = "astaxie_" ## Tipos elementares ### Booleano Em Go, utilizamos `bool` para definir uma variável do tipo booleano, sendo que o valor só pode ser `true` ou `false`, e o valor padrão será `false`. ( ***Você não pode converter tipos de variáveis' entre número e booleano!*** ) // código de amostra var isActive bool // variável global var enabled, disabled = true, false // omite o tipo das variáveis func test() { var available bool // variável local valid := false // declaração curta de variável available = true // atribui um valor à variável } ### Tipos numéricos Tipos inteiros incluem tipos com sinais e sem sinais. Go possui os tipos `int` e `uint`, os quais possuem o mesmo comprimento, porém, o comprimento específico depende do sistema operacional. Eles utilizam 32 bits em sistemas operacionais 32 bits e 64 bits em sistemas operacionais de 64 bits. Go também têm tipos que possuem comprimentos específicos, incluindo `rune`, `int8`, `int16`, `int32`, `int64`, `byte`, `uint8`, `uint16`, `uint32`, `uint64`. Note que `rune` é um pseudônimo de `int32` e `byte` é um pseudônimo de `uint8`. Uma coisa importante que você deve saber é que você não pode atribuir valores entre estes tipos, esta operação irá gerar erros de compilação. var a int8 var b int32 c := a + b Embora int32 tenha um comprimento maior do que int8, e possui o mesmo tipo int, você não pode atribuir valores entre eles. ( ***neste caso, c será declarado como tipo `int`*** ) Tipos float possuem os tipos `float32` e `float64` e nenhum tipo chamado `float`. O último é o tipo padrão utilizado em declarações curtas. Isto é tudo? Não! Go também suporta números complexos. `complex128` (com uma parte de 64 bits real e uma parte de 64 bits imaginária) é o tipo padrão, mas se você precisa de um tipo menor, existe um tipo chamado `complex64` (com uma parte de 32 bits real e uma parte de 32 bits imaginária). Sua forma é `RE+IMi`, onde `RE` é a parte real e `IM` é a parte imaginária, e o último `i` é o número imaginário. Há um exemplo de número complexo. var c complex64 = 5+5i // saída: (5+5i) fmt.Printf("Value is: %v", c) ### String Nós falamos apenas sobre como a linguagem Go utiliza o conjunto de caracteres UTF-8. As strings são representadas por aspas duplas `""` ou crases (backticks) ``` `` ```. // código de amostra var frenchHello string // forma básica de definir string var emptyString string = "" // define uma string vazia func test() { no, yes, maybe := "no", "yes", "maybe" // declaração curta japaneseHello := "Ohaiou" frenchHello = "Bonjour" // forma básica de atribuir valores } É impossível alterar valores de string pelo índice. Você receberá erros ao compilar o seguinte código. var s string = "hello" s[0] = 'c' E se eu realmente quiser alterar apenas um caractere de uma string? Tente o seguinte código. s := "hello" c := []byte(s) // converte string para o tipo []byte c[0] = 'c' s2 := string(c) // converte novamente para o tipo string fmt.Printf("%s\n", s2) Use o operador `+` para combinar duas strings. s := "hello," m := " world" a := s + m fmt.Printf("%s\n", a) e também. s := "hello" s = "c" + s[1:] // você não pode alterar valores da string pelo índice, mas você pode obter os valores fmt.Printf("%s\n", s) E se eu quiser ter uma string de múltiplas linhas? m := `hello world` `` ` não irá escapar nenhum caractere da string. ### Tipos error Go possui um tipo `error` com o propósito de lidar com mensagens de erro. Há também um pacote chamado `errors` para lidar com os erros. err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) } ### Estrutura de dados subjacente A seguinte imagem vem de um artigo sobre [estruturas de dados em Go](http://research.swtch.com/godata) do [Blog Russ Cox](http://research.swtch.com/). Como você pode ver, Go utiliza blocos de memória para armazenar dados. ![](images/2.2.basic.png?raw=true) Figure 2.1 Estrutura de dados subjacente em Go ## Algumas habilidades ### Definir por grupo Se você deseja definir múltiplas constantes, variáveis ou importar pacotes, você pode utilizar uma forma de grupo. Forma básica. import "fmt" import "os" const i = 100 const pi = 3.1415 const prefix = "Go_" var i int var pi float32 var prefix string Forma de grupo. import( "fmt" "os" ) const( i = 100 pi = 3.1415 prefix = "Go_" ) var( i int pi float32 prefix string ) A menos que você atribua, o valor da constante é `iota`, o primeiro valor de constante no grupo `const()` será `0`. Se as constantes seguintes não atribuirem valores explicitamente, seus valores serão iguais ao último. Se o valor da última constante é `iota`, os valores das constantes seguintes que não são atribuídas será `iota` também. ### Enumeração iota Go possui uma palavra-chave chamada `iota`, esta palavra-chave é utilizada para fazer `enum`, ela começa com `0` e aumenta de 1 em 1. const( x = iota // x == 0 y = iota // y == 1 z = iota // z == 2 w // se não há nenhuma expressão após o nome da constate, ele usa a última expressão, ou seja, está definindo w = iota implicitamente. Portanto, w == 3, e y e x podem omitir "= iota" também. ) const v = iota // uma vez que iota encontra a palavra-chave `const`, ela é redefinida para `0`, então v = 0. const ( e, f, g = iota, iota, iota // e=0,f=0,g=0 valores de iota são iguais em uma linha. ) ### Algumas regras A razão de Go ser concisa é porque ela possui alguns comportamentos padrão. - Qualquer variável que começa com uma letra maiúscula significa que ela será exportada, caso contrário será privada. - A mesma regra se aplica para funções e constantes, sendo que não existem as palavras-chave `public` ou `private` em Go. ## array, slice, map ### array `array` é um array obviamente, e nós definimos ele da seguinte maneira. var arr [n]type em `[n]type`, `n` é o comprimento do array, enquanto `type` é o tipo dos elementos contidos no array. Assim como em outras linguagens, nós utilizamos `[]` para obter ou definir valores de elementos no array. var arr [10]int // um array de tipo [10]int arr[0] = 42 // array é baseado em 0 arr[1] = 13 // atribuir um valor ao elemento fmt.Printf("The first element is %d\n", arr[0]) // obtém o valor do elemento, irá retornar 42 fmt.Printf("The last element is %d\n", arr[9]) // retorna o valor padrão do elemento da posição 10 do array, que, neste caso, é 0. Como o comprimento faz parte do tipo do array, `[3]int` e `[4]int` são tipos diferentes, portanto, não podemos alterar o comprimento dos arrays. Quando você utiliza arrays como argumentos, as funções obtêm suas copias em vez de referências! Se você deseja utilizar referências, você pode utilizar `slice`. Falaremos sobre isto mais tarde. É possível utilizar `:=` quando você define arrays. a := [3]int{1, 2, 3} // define um array de inteiros com 3 elementos b := [10]int{1, 2, 3} // define um array de inteiros com 10 elementos, dos quais os 3 primeiros são atribuídos. O restante deles recebe o valor padrão 0. c := [...]int{4, 5, 6} // usa `…` para substituir o parâmetro de comprimento e a Go irá calcular isto para você. Você pode querer utilizar arrays como elementos de arrays'. Vamos ver como fazer isto. // define um array bidimensional com 2 elementos, e cada elemento possui 4 elementos doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}} // A declaração pode ser escrita de forma mais concisa da seguinte forma. easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}} Array estrutura de dados subjacente. ![](images/2.2.array.png?raw=true) Figure 2.2 Relação de mapeamento de array multidimensional ### slice Em várias situações, o tipo array não é uma boa escolha -por exemplo quando não sabemos o comprimento que o array terá quando o definimos. Sendo assim, precisamos de um "array dinâmico". Isto é chamado de `slice` em Go. `slice` não é realmente um `array dinâmico`. É um tipo de referência. `slice` aponta para um `array` subjacente cuja declaração é semelhante ao `array`, porém não precisa de um comprimento preestabelecido. // definimos um slice assim como definimos um array, mas desta vez, omitimos o comprimento var fslice []int Então nós definimos um `slice` e inicializamos seus dados. slice := []byte {'a', 'b', 'c', 'd'} `slice` pode redefinir slices ou arrays existentes. `slice` usa `array[i:j]` para "cortar", onde `i` é o índice de início e `j` é o índice final, mas observe que o valor de `array[j]` não será incluído no slice, pois o comprimento da fatia é `j-i`. // define um array com 10 elementos cujos tipos são bytes var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // define dois slices com o tipo []byte var a, b []byte // 'a' aponta para os elementos da terceira até a quinta posição do array ar a = ar[2:5] // agora 'a' possui os elementos ar[2], ar[3] e ar[4] // 'b' é outro slice do array ar b = ar[3:5] // agora 'b' possui os elementos ar[3] e ar[4] Observe as diferenças entre `slice` e `array` quando você define eles. Nós utilizamos `[…]` para deixar a linguagem Go calcular o comprimento, mas utilizamos `[]` para definir um slice. Sua estrutura de dados subjacente. ![](images/2.2.slice.png?raw=true) Figure 2.3 Relação enter slice e array `slice` possui algumas operações convenientes. - `slice` é baseado em 0, `ar[:n]` igual a `ar[0:n]` - Se omitido, o segundo índice será o comprimento do `slice`, `ar[n:]` igual a `ar[n:len(ar)]`. - Você pode usar `ar[:]` para "cortar" o array inteiro, as razões são explicadas nas duas primeiras declarações. Mais exemplos referentes a `slice` // define um array var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // define dois slices var aSlice, bSlice []byte // algumas operações convenientes aSlice = array[:3] // igual a aSlice = array[0:3] aSlice possui os elementos a,b,c aSlice = array[5:] // igual a aSlice = array[5:10] aSlice possui os elementos f,g,h,i,j aSlice = array[:] // igual a aSlice = array[0:10] aSlice possui todos os elementos // slice de um slice aSlice = array[3:7] // aSlice possui os elementos d,e,f,g,len=4,cap=7 bSlice = aSlice[1:3] // bSlice contém aSlice[1], aSlice[2], então têm os elementos e,f bSlice = aSlice[:3] // bSlice contém aSlice[0], aSlice[1], aSlice[2], então têm os elementos d,e,f bSlice = aSlice[0:5] // slice pode ser expandido no intervalo de cap, agora bSlice contém d,e,f,g,h bSlice = aSlice[:] // bSlice têm os mesmos elementos do slice aSlice, os quais são d,e,f,g `slice` é um tipo de referência, portanto, qualquer alteração irá afetar outras variáveis que apontam para o mesmo slice ou array. Por exemplo, no caso dos slices `aSlice` e `bSlice` apresentado acima, se você alterar o valor de um elemento em `aSlice`, `bSlice` também será alterado. `slice` é como uma estrutura por definição, e contém 3 partes. - Um ponteiro que aponta para onde o `slice` inicia. - O comprimento do `slice`. - Capacidade, o comprimento do índice de início para o índice final do `slice`. Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} Slice_a := Array_a[2:5] Segue a seguir a estrutura subjacente do código acima. ![](images/2.2.slice2.png?raw=true) Figure 2.4 Informações do slice de um array Existem algumas funções incorporadas no slice. - `len` obtém o comprimento do `slice`. - `cap` obtém o comprimento máximo do `slice`. - `append` acrescenta um ou mais elementos ao `slice`, e retorna um `slice`. - `copy` copia elementos de um slice para outro e retorna o número de elementos que foram copiados. Atenção: `append` irá alterar o array para onde o `slice` aponta e afetará também outros slices que apontam para o mesmo array. Além disto, se não houver comprimento suficiente para o slice (`(cap-len) == 0`), `append` retorna um novo array para o slice. Quando isto acontece, outros slices que estão apontando para o array antigo não serão afetados. ### map `map` se comporta como um dicionário em Python. Use a forma `map[keyType]valueType` para defini-lo. Vamos ver um pouco de código. Os valores 'set' e 'get' em `map` são semelhantes ao `slice`, no entanto, o índice no `slice` só pode ser do tipo 'int' enquanto `map` pode usar muitos outros tipos, como por exemplo: `int`, `string`, ou qualquer outro que você quiser. Além disto, eles são capazes de usar `==` e `!=` para comparar valores. // use string como o tipo chave, int como o tipo valor e `make` para inicializar. var numbers map[string] int // outra forma de definir map numbers := make(map[string]int) numbers["one"] = 1 // atribui valor por chave numbers["ten"] = 10 numbers["three"] = 3 fmt.Println("The third number is: ", numbers["three"]) // obtém valores // Isto imprime: The third number is: 3 Algumas notas sobre o uso de map. - `map` é desordenado. Toda vez que você imprime `map` você terá resultados diferentes. É impossível obter valores por `índice` -você precisa utilizar `chave`. - `map` não tem um comprimento fixo. É um tipo de referência, assim como o `slice`. - `len` também funciona para `map`. Isto retorna quantas `chave`s o map tem. - É muito fácil alterar valores utilizando `map`. Basta usar `numbers["one"]=11` para alterar o valor da `chave` one para `11`. Você pode usar a forma `chave:valor` para inicializar valores em um `map`, pois `map` possui métodos embutidos para verificar se a `chave` existe. Use `delete` para deletar um elemento em um `map`. // Inicializa um map rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 } // map possui dois valores de retorno. Caso a chave não exista, o segundo valor de retorno, neste caso recebido pela variável 'ok', será falso (false). Caso contrário será verdadeiro (true). csharpRating, ok := rating["C#"] if ok { fmt.Println("C# is in the map and its rating is ", csharpRating) } else { fmt.Println("We have no rating associated with C# in the map") } delete(rating, "C") // deleta o elemento com a chave "c" Como foi dito acima, `map` é um tipo de referência. Se dois `map`s apontam para o mesmo dado subjacente, qualquer alteração irá afetar ambos. m := make(map[string]string) m["Hello"] = "Bonjour" m1 := m m1["Hello"] = "Salut" // agora o valor de m["hello"] é Salut ### make, new `make` realiza alocação de memória para modelos internos, como `map`, `slice`, e `channel`, enquanto `new` é utilizado para alocação de memória de tipos. `new(T)` aloca a memória para o valor zero do tipo `T` e retorna seu endereço de memória, que é o valor do tipo `*T`. Por definição, isto retorna um ponteiro que aponta para o valor zero de `T`. `new` retorna ponteiros. A função interna `make(T, args)` possui diferentes propósitos se comparado a `new(T)`. `make` pode ser usado para `slice`, `map`, e `channel`, e retorna o tipo `T` com um valor inicial. A razão para fazer isto é porque os dados subjacentes destes três tipos devem ser inicializados antes de apontar para eles. Por exemplo, um `slice` contém um ponteiro que aponta para um `array` subjacente, comprimento e capacidade. Antes que estes dados sejam inicializados, `slice` é `nil`, então, para `slice`, `map` e `channel`, `make` inicializa seus dados subjacentes e atribui alguns valores adequados. `make` retorna valores diferentes de zero. A seguinte imagem mostra como `new` e `make` são diferentes. ![](images/2.2.makenew.png?raw=true) Figure 2.5 Alocação de memória para make e new Valor zero não significa valor vazio. Na maioria dos casos é o valor padrão das variáveis. Aqui está uma lista de alguns valores zero. int 0 int8 0 int32 0 int64 0 uint 0x0 rune 0 // o tipo real de rune é int32 byte 0x0 // o tipo real de byte é uint8 float32 0 // comprimento é 4 bytes float64 0 // comprimento é 8 bytes bool false string "" ## Links - [Sumário](preface.md) - Seção anterior: ["Hello, Go"](02.1.md) - Próxima seção: [Declarações de controle e funções](02.3.md)