Programando Em Go - Crie Aplicações Com a Linguagem Do Google

Embed Size (px)

Citation preview

  • Casa do CdigoTodos os direitos reservados e protegidos pela Lei n9.610, de10/02/1998.Nenhuma parte deste livro poder ser reproduzida, nem transmitida, semautorizao prvia por escrito da editora, sejam quais forem os meios:fotogrficos, eletrnicos, mecnicos, gravao ou quaisquer outros.

    Casa do CdigoLivros para o programadorRua Vergueiro, 3185 - 8 andar04101-300 Vila Mariana So Paulo SP Brasil

  • Casa do Cdigo

    Crditos

    O Gopher utilizado na capa deste livro criao de Renee French (http://reneefrench.blogspot.com/) e licenciado sobCreative CommonsAttributions3.0 (https://creativecommons.org/licenses/by/3.0/) .

    i

  • Casa do Cdigo

    Prefcio

    Go tem sido fundamental no meu dia a dia escrevendo programas concor-rentes e plataformas (systems programming). uma linguagem que favorecea criao de programas paralelos, leves, rpidos, simples de entender, de dis-tribuir (um simples binrio compilado estaticamente) e de manter.

    Honestamente, porm, minhas primeiras impresses gerais sobre a lin-guagem no foram das melhores. A ausncia de funcionalidades encontradasem outras linguagens mais sofisticadas me incomodava. Algumas bem po-lmicas e controversas, como a de tipos genricos e o tratamento de errossimples sem excees. No incomum ter que escrever uma ou outra linhade cdigo a mais em Go do que seria necessrio em outra linguagem. No uma linguagem otimizada para programas curtos, mas sim para programasescalveis.

    No incio, o modo com que Go lida com concorrncia e paralelismo foi oque despertoumeu interesse na linguagem. Channels e goroutines so primiti-vas extremamente poderosas que continuam influenciando bastante a formacom que escrevo programas concorrentes, inclusive em outras linguagens. Eeu tenho certeza de que vo influenciar a forma com que voc escreve pro-gramas tambm.

    O resto no parecia ter nada de especial quando comparado com outraslinguagens. Mas, com o tempo, fui aprendendo a apreciar a simplicidade deGo. Demorou um pouco at me cair a ficha de que a ausncia de funcionali-dades que trariam complexidade foi (e continua sendo) uma deciso explcitade seus principais mantenedores. Isso faz com que Go seja uma linguagemrelativamente fcil de aprender e geralmente existe apenas uma (ou poucas)forma(s) clara(s) de se resolver os problemas e escrever cdigo emGo, a mai-

    iii

  • Casa do Cdigo

    oria delas usando apenas o que j est disponvel na biblioteca padro.Quando me d vontade de reclamar de que Go no tem essa ou aquela

    funcionalidade, lembro-me o quo simples ler e entender programas escri-tos em Go. Lembro-me tambm de quanta discusso desnecessria se evitano dia a dia com outros programadores sobre como cdigo deveria ser es-crito. Uma vez que se entende e aceita a mentalidade por trs da linguagem,times conseguem focar em escrever cdigo que resolve problemas de verdadede forma simples de manter, em vez de gastar tempo discutindo prefernciaspessoais e de estilo de cdigo.

    Go, na minha opinio, representa um timo balano entre pragmatismoe funcionalidades. uma escolha perfeita para programas que precisam so-breviver por muito tempo, na mo de muitas pessoas e times diferentes.

    J tive o prazer de trabalhar diretamente com o Caio, que um excelenteprogramador e faz realmente um bom trabalho em apresentar a linguagem.

    Bom proveito e feliz programao!Fabio Kung

    iv

  • Casa do Cdigo

    Agradecimentos

    minha esposa Gabriela, pela pacincia e todo o apoio.Ao Francisco Capuano, pelo exemplo e inspirao. minha av Arlinda e ao meu tio Washington, por permitirem que eu

    pudesse estudar e me apaixonar pela minha profisso.A toda a minha famlia por acreditar e confiar em mim, sempre.E a todos que, de forma direta ou indireta, contriburam para a escrita

    deste livro; em especial: Guilherme Conte, Anderson Leite, Reinaldo Braga,Bruno Grasselli, Luca Pette, Adriano Almeida e Paulo Silveira.

    v

  • Casa do Cdigo Sumrio

    Sumrio

    1 Introduo 11.1 Por que Go? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 Instalao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.3 O primeiro programa em Go . . . . . . . . . . . . . . . . . . . 7

    2 Explorando a sintaxe bsica 92.1 Estrutura do captulo . . . . . . . . . . . . . . . . . . . . . . . 92.2 If e expresses lgicas . . . . . . . . . . . . . . . . . . . . . . . 102.3 Arrays e slices . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.4 Exemplo 1: conversor de medidas . . . . . . . . . . . . . . . . 102.5 Criando funes bsicas . . . . . . . . . . . . . . . . . . . . . 172.6 Exemplo 2: quicksort e funes . . . . . . . . . . . . . . . . . 18

    3 Indo alm: mais exemplos 273.1 Exemplo 3: mapas e estatsticas . . . . . . . . . . . . . . . . . 273.2 Exemplo 4: pilhas e tipos customizados . . . . . . . . . . . . . 31

    4 Colees: arrays, slices e maps 394.1 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394.2 Slices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424.3 Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

    vii

  • Sumrio Casa do Cdigo

    5 Criando novos tipos 635.1 Novos nomes para tipos existentes . . . . . . . . . . . . . . . 635.2 Converso entre tipos compatveis . . . . . . . . . . . . . . . 655.3 Criando abstraes mais poderosas . . . . . . . . . . . . . . . 675.4 Structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705.5 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735.6 Duck typing e polimorfismo . . . . . . . . . . . . . . . . . . . 765.7 Um exemplo da biblioteca padro: io.Reader . . . . . . . . . 78

    6 Funes 836.1 A forma bsica . . . . . . . . . . . . . . . . . . . . . . . . . . . 836.2 Valores de retorno nomeados . . . . . . . . . . . . . . . . . . 856.3 Argumentos variveis . . . . . . . . . . . . . . . . . . . . . . . 876.4 Funes de primeira classe . . . . . . . . . . . . . . . . . . . . 896.5 Funes annimas . . . . . . . . . . . . . . . . . . . . . . . . . 906.6 Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 926.7 Higher-order functions . . . . . . . . . . . . . . . . . . . . . . . 946.8 Tipos de funo . . . . . . . . . . . . . . . . . . . . . . . . . . 966.9 Servindo HTTP atravs de funes . . . . . . . . . . . . . . . 99

    7 Concorrncia com goroutines e channels 1037.1 Goroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047.2 Channels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067.3 Buffers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1087.4 Controlando a direo do fluxo . . . . . . . . . . . . . . . . . 1117.5 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1117.6 Temporizadores e timeouts . . . . . . . . . . . . . . . . . . . . 1157.7 Sincronizando mltiplas goroutines . . . . . . . . . . . . . . . 1177.8 Concorrncia, paralelismo e GOMAXPROCS . . . . . . . . . 1197.9 Recapitulando . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

    viii

  • Casa do Cdigo Sumrio

    8 Mo na massa: encurtador de URLs 1238.1 Estrutura do projeto . . . . . . . . . . . . . . . . . . . . . . . . 1248.2 Criando o servidor . . . . . . . . . . . . . . . . . . . . . . . . . 1248.3 Criando URLs curtas . . . . . . . . . . . . . . . . . . . . . . . 1268.4 Redirecionando para as URLs originais . . . . . . . . . . . . . 1318.5 Apresentando o pacote url . . . . . . . . . . . . . . . . . . . . 1338.6 Especificando a implementao do repositrio . . . . . . . . 1348.7 Criando identificadores curtos . . . . . . . . . . . . . . . . . . 1358.8 Implementando o repositrio em memria . . . . . . . . . . 138

    9 Compilando e executando o projeto 1419.1 Entendendo o processo de compilao . . . . . . . . . . . . . 1429.2 Instalando o executvel no sistema . . . . . . . . . . . . . . . 1439.3 Aprendendo mais . . . . . . . . . . . . . . . . . . . . . . . . . 144

    10 Colhendo estatsticas 14510.1 Realizando a contagem no repositrio . . . . . . . . . . . . . 14610.2 Registrando os acessos no servidor . . . . . . . . . . . . . . . 14710.3 Serializando JSON . . . . . . . . . . . . . . . . . . . . . . . . . 15110.4 Visualizando as estatsticas como JSON . . . . . . . . . . . . 153

    11 Refatorando o cdigo 15911.1 Substituindo variveis globais . . . . . . . . . . . . . . . . . . 16011.2 Reduzindo a duplicao de cdigo . . . . . . . . . . . . . . . 16311.3 Escrevendo logs . . . . . . . . . . . . . . . . . . . . . . . . . . 16511.4 Flexibilizando a inicializao do servidor . . . . . . . . . . . . 167

    12 Prximos passos 17112.1 Aprendendo mais . . . . . . . . . . . . . . . . . . . . . . . . . 172

    Bibliografia 173

    ix

  • Captulo 1

    Introduo

    Desenvolver um software nunca foi uma tarefa fcil. Escrever um programa,por menor que ele seja, exige altas doses de concentrao, pacincia e atenoaos detalhes. Por vezes os problemas parecem no ter soluo. E nada dissoparece ter mudado muito com o passar do tempo: novas linguagens de pro-gramao e novas ferramentas so criadas todos os dias por programadoresao redor do mundo, enquanto o nmero de desafios no diminui.

    Ao contrrio, numa poca em que processadores com mltiplos ncleos(multi-core processors) esto presentes at mesmo em dispositivos mveis,programadores e administradores de sistemas sofrem para tentar usar aom-ximo os recursos da mquina sem que haja impacto para seus usurios ta-refa que se torna mais difcil conforme o nmero de usurios cresce.

    No existe nenhuma linguagem de programao capaz de resolver todosestes problemas de forma fcil. No entanto, a linguagem Go surgiu num am-biente onde tais problemas so ainda mais extremos: o Google.

  • 1.1. Por que Go? Casa do Cdigo

    Apesar de bastante inspirada na linguagem C, Go possui caractersticasde mais alto nvel, como abstraes para algumas estruturas de dados, coletade lixo (garbage collection) e duck typing, alm de trazer uma abordagemmo-derna e elegante para a criao de aplicaes concorrentes. Go tambm incluiuma extensa biblioteca padro com ferramentas para comunicao em redes,servidores HTTP, expresses regulares, leitura e escrita de arquivos e muitomais.

    O objetivo deste livro apresentar ao leitor os recursos da linguagemGo eimportantes partes da biblioteca padro, sempre trazendo exemplos relevan-tes que demonstram o uso de cada recurso.

    Resolver os problemas citados anteriormente um desafio enorme, masGo veio para tornar esta tarefa um pouco mais prazerosa.

    1.1 Por queGo?Go nasceu como um projeto interno no Google, iniciado em 2007 por RobPike, Ken Thompson e Robert Griesemer, e posteriormente lanado comoum projeto de cdigo aberto em novembro de 2009. Pike e Thompson jhaviam trabalhado juntos noBell Labs, o berodoUnix e doPlan 9 o sistemaoperacional que deu origem s ferramentas de compilao usadas como basedo desenvolvimento da linguagem Go.

    No artigo (em ingls) Go at Google: Language Design in the Service ofSoftware Engineering (http://talks.golang.org/2012/splash.article) , Rob Pikeatribui os longos perodos de compilao e a dificuldade em escalar o desen-volvimento de grandes aplicaes como sendo os principaismotivadores paraa criao da nova linguagem. Segundo ele, a maior parte do problema aforma com que as linguagens C e C++ tratam o gerenciamento de dependn-cias entre os diversos arquivos-fonte.

    Dentro deste cenrio, Go foi concebida com o objetivo de tornar o desen-volvimento de servidores no Google uma tarefa mais produtiva e eficiente.

    Para evitar os problemas de compilao, Go implementa um controle ri-goroso e inteligente de dependncias, baseado na definio e uso de packages(pacotes).

    Com uma sintaxe bastante limpa, se comparada com outras linguagens

    2

  • Casa do Cdigo Captulo 1. Introduo

    inspiradas em C como C++ e Java , Go permite a escrita de programasconcisos e legveis, alm de facilitar bastante a escrita de ferramentas que in-teragem com o cdigo-fonte. Bons exemplos de tais ferramentas so go fmt(formata o cdigo de acordo com o guia de estilo da linguagem) e go fix(reescreve partes do cdigo que usa APIs depreciadas para que usem as novasAPIs introduzidas em verses mais recentes).

    Go possui tipagem forte e esttica, porm introduz uma forma curta dedeclarao de variveis baseada em inferncia de tipos, evitando redundn-cia e produzindo cdigo muito mais sucinto do que linguagens estaticamentetipadas tradicionais. Alm disso, Go traz uma implementao de duck typing(se faz quack como um pato e anda como um pato, ento provavelmente um pato) baseada em interfaces, permitindo a criao de tipos bastante flex-veis.

    Alguns tipos de coleo de dados como slices (listas de tamanho din-mico) e maps (dicionrios de dados associativos) so nativos linguagem,que tambm fornece um conjunto de funes embutidas para a manipulaodestes tipos; arrays (listas de tamanho fixo) tambm esto disponveis paracasos em que um nvel maior de controle necessrio.

    Go suporta o uso de ponteiros (referncias a endereos de memria), oque torna ainda mais fcil a criao de poderosos tipos customizados. Entre-tanto, aritmtica sobre ponteiros no suportada. Apesar de toda a flexibi-lidade, atravs do uso de um coletor de lixo (ou garbage collector), Go reduzdrasticamente a complexidade no gerenciamento de memria das aplicaes,tornando-as mais robustas e evitando vazamentos descuidados.

    Go tambm fornece recursos que permitem a escrita de programas total-mente procedurais, orientados a objetos (atravs da definio de novos tipos ede funes que operam sobre tais tipos) ou funcionais funes somembrosde primeira classe em Go, que tambm suporta a criao de closures (funesque herdam o contexto de onde elas foram definidas).

    A abordagem de Go para concorrncia um dos maiores diferenciais dalinguagem. Inspirada no famoso paper de C. A. R. Hoare Communicating Se-quential Processes [2], Go implementa goroutines processos extremamenteleves que se comunicam atravs de channels, evitando o uso dememria com-partilhada e dispensando o uso de travas, semforos e outras tcnicas de sin-

    3

  • 1.2. Instalao Casa do Cdigo

    cronizao de processos.Com todos estes recursos disponveis, chegou a hora de ver comoGo fun-

    ciona na prtica.

    1.2 InstalaoO primeiro passo para comear a escrever programas em Go instalar a lin-guagem e suas ferramentas. Os pacotes de distribuio esto disponveis nosite http://golang.org/dl/. No momento da escrita deste livro, a verso est-vel disponvel a 1.3. Faa o download do pacote referente ao seu sistemaoperacional. A seguir voc encontrar as instrues especficas para cada pla-taforma.

    Linux

    Abra um terminal, v para o diretrio onde voc salvou o arquivo dadistribuio (no caso do Linux, o nome do arquivo ser parecido comgo1.3.linux-amd64.tar.gz) e execute o comando a seguir:

    sudo tar -C /usr/local -xzf go1.3.linux-amd64.tar.gz

    As ferramentas da linguagem Go agora estaro disponveis no diretrio/usr/local/go. Adicione o diretrio /usr/local/go/bin varivel deambiente PATH para ter acesso s ferramentas, independente do diretrioonde voc estiver no sistema. Voc pode fazer isso adicionando a seguintelinha ao seu arquivo $HOME/.profile:

    export PATH=$PATH:/usr/local/go/bin

    Para verificar se a instalao est correta, execute o seguinte comando noterminal:

    $ go version

    Este comando dever produzir uma sada similar seguinte:

    go version go1.3 linux/amd64

    4

  • Casa do Cdigo Captulo 1. Introduo

    Mac OS

    No Mac OS h duas formas de instalao. A mais simples idntica noLinux, sendo a nica diferena o nome do arquivo de distribuio:

    sudo tar -C /usr/local -xzf go1.3.darwin-amd64-osx10.8.tar.gz

    A outra opo baixar o instalador automticogo1.3.darwin-amd64-osx10.8.pkg. Execute-o e siga as instrues.

    As duas formas disponibilizam as ferramentas instaladas no diretrio/usr/local/go. Aps a instalao, assim como no Linux, adicione o di-retrio /usr/local/go/bin varivel de ambiente PATH no seu arquivo$HOME/.profile:

    export PATH=$PATH:/usr/local/go/bin

    E verifique se a instalao est correta, executando o seguinte comandono terminal:

    $ go version

    Este comando dever produzir uma sada similar ao que segue:

    go version go1.3 darwin/amd64

    Windows

    No Windows, assim como no Mac OS, h duas formas de instalao. Aforma recomendada a utilizao do instalador automtico. Faa o downloaddo arquivo go1.3.windows-amd64.msi, execute-o e siga as instruespasso a passo. Aps a instalao, as variveis de ambiente sero automatica-mente configuradas e voc poder seguir para as instrues de ps-instalao.

    A forma alternativa fazer o download do pacotego1.3.windows-amd64.zip e descompact-lo no diretrio C:\Go.Em seguida, adicione o diretrio C:\Go\bin varivel de ambiente PATH.

    Para ter certeza de que a instalao foi bem-sucedida, execute o seguintecomando no prompt:

    C:\>go version

    5

  • 1.2. Instalao Casa do Cdigo

    Este comando dever produzir uma sada similar seguinte:

    go version go1.3 windows/amd64

    Instalando Go em um diretrio no-padro

    Todas as instrues apresentadas anteriormente partem do princpio deque a instalao foi feita no diretrio padro /usr/local/go no Linuxe Mac OS, e C:\Go no Windows. Caso voc no tenha esta opo ou pre-fira realizar a instalao em um diretrio diferente, algumas configuraesadicionais so necessrias.

    Para que tudo funcione como esperado, preciso configurar a varivelde ambiente GOROOT para referenciar o diretrio escolhido para a instala-o. Por exemplo, se a instalao foi feita no diretrio $HOME/go no Linux,adicione a seguinte configurao ao seu $HOME/.profile:

    export GOROOT=$HOME/goexport PATH=$PATH:$GOROOT/bin

    NoWindows, uma configurao semelhante deve ser feita.Entretanto, para evitar problemas, recomendvel que o diretrio padro

    seja utilizado.

    Ps-instalao

    A partir deste momento, todos os exemplos de comandos sero escritosutilizando a notaoUnix, assumindo que voc tem uma instalao funcionalda linguagem Go e que o diretrio GOROOT/bin foi adicionado varivelPATH (sendo que GOROOT deve ser o diretrio no qual Go foi instalada).

    Para extrair o mximo das ferramentas disponveis na instalao, preci-samos criar um diretrio de trabalho (workspace) onde todos os programasdevem residir. A estrutura do workspace inclui trs subdiretrios:

    src contm o cdigo-fonte de todos os seus programas escritos emGo (organizados em pacotes), e tambm o cdigo-fonte de pacotes deterceiros instalados em seu sistema;

    pkg contm objetos referentes aos pacotes;

    6

  • Casa do Cdigo Captulo 1. Introduo

    bin contm arquivos executveis gerados no seu sistema.

    Crie um workspace e adicione-o varivel de ambiente GOPATH:

    $ mkdir $HOME/go

    Para maior convenincia, configure a varivel GOPATH no seu ar-quivo $HOME/.profile (ou nas configuraes avanadas do seu Win-dows conforme mencionado anteriormente), e adicione tambm o diretrioGOPATH/bin varivel de ambiente PATH:

    export GOPATH=$HOME/goexport PATH=$PATH:$GOPATH/bin

    1.3 O primeiro programa emGoProgramas emGo podem ser escritos em qualquer editor de textos que tenhasuporte codificao UTF-8. Os arquivos-fontes devem necessariamenteser salvos nesta codificao, caso contrrio o compilador no ser capaz deprocess-los.

    Agora podemos finalmente escrever nosso primeiro programa em Go.Crie um novo diretrio chamado cap1-ola dentro de $GOPATH/src. Nodiretrio recm-criado, utilize seu editor de textos favorito e crie um novoarquivo chamado ola.go com o seguinte contedo e salve o arquivo:

    package main

    import "fmt"

    func main() {fmt.Println("Ol, Go!")

    }

    Para executar o programa, utilizaremos a ferramenta go. V para o dire-trio $GOPATH/src e execute o seguinte comando:

    $ go run cap1-ola/ola.go

    7

  • 1.3. O primeiro programa em Go Casa do Cdigo

    Aps a execuo do programa, o texto Ol, Go! dever ser impresso noterminal.

    Arquivos-fonte contendo cdigo Go devem seguir sempre a mesma es-trutura, dividida em trs sees: primeiro, a declarao do pacote, seguidada declarao dos pacotes externos dos quais o arquivo-fonte depende, e porfim, o cdigo referente ao programa ou pacote sendo escrito.

    Todo cdigo Go deve obrigatoriamente existir dentro de um pacote (pac-kage), e todo programa em Go deve ter um pacote main contendo uma fun-o main() que serve como ponto de partida do programa.

    No exemplo anterior, importamos o pacote fmt, que contm funespara a formatao de strings, e utilizamos a funo Println paramostraro texto Ol, Go! para o usurio.

    Repare que a funo main() no recebe nenhum argumento e no re-torna nenhum valor. Diferente de linguagens como Java e C, argumentos pas-sados para um programa Go atravs da linha de comando no so automati-camente disponibilizados ao programa. Em vez disso, precisamos utilizar umpacote chamado os, que ser apresentado posteriormente.

    Agora j temos um ambiente de desenvolvimento funcional, e acabamosde escrever o primeiro programa em Go.

    Nos captulos 2 e 3, veremos alguns exemplos que apresentaro a sintaxebsica e alguns dos recursos da linguagem. Nos captulos posteriores, entra-remos em detalhes sobre as funcionalidades mais importantes.

    8

  • Captulo 2

    Explorando a sintaxe bsica

    Agora que voc j tem um ambiente capaz de compilar e executar programasemGo, vamos fazer umpequeno passeio sobre as principais caractersticas dalinguagem com alguns exemplos guiados. O objetivo dos exemplos a seguir familiarizar o leitor com os elementos mais comuns da sintaxe, as princi-pais palavras reservadas e estruturas de controle, alm de apresentar algunsidiomas comuns no desenvolvimento de aplicaes em Go.

    Todos os exemplos de cdigo deste livro esto disponveis para consultano endereo https://github.com/caiofilipini/casadocodigo-go.

    Os recursos apresentados nos exemplos sero explicados em maiores de-talhes nos prximos captulos.

    2.1 Estrutura do captuloEste captulo baseado em dois exemplos.

  • 2.2. If e expresses lgicas Casa do Cdigo

    O primeiro um simples conversor de medidas que apresentar o uso dealguns elementos bsicos da linguagem, como as estruturas de controle if efor, o conceito de slices e tratamento bsico de erros. Este exemplo pode serencontrado na seo 2.4.

    O segundo, encontrado na seo 2.6, uma implementao do algoritmode ordenao quicksort, e introduz o uso de funes, recursividade e a funoappend().

    Antes de apresentar os exemplos, porm, veremos o funcionamento b-sico das expresses if e o que so arrays e slices.

    2.2 If e expresses lgicasEm Go, diferente de algumas linguagens, somente expresses de valor lgicoverdadeiro ou falso podem ser utilizadas em conjunto com instrues if.

    Estas expresses devem necessariamente ser do tipo bool cujos ni-cos valores possveis so true e false e podem ser variveis ou mesmofunes ou mtodos, desde que retornem um valor do tipo bool.

    2.3 Arrays e slicesGo possui dois tipos padres para listas de dados: arrays e slices.

    Um array uma lista de tamanho fixo, similar a um array nas lingua-gens C ou Java.

    J um slice uma camada de abstrao criada com base nos arrayse pode crescer indefinidamente, proporcionando uma flexibilidade muitomaior.

    As diferenas entre eles sero discutidas em detalhes no captulo 4.

    2.4 Exemplo 1: conversor de medidasOprimeiro exemplo um simples conversor de medidas. Ele aceita como en-trada uma lista de valores com sua unidade de medida, e produz uma lista devalores convertidos. Por questes didticas, o conversor trabalha apenas comdois tipos de converses: de graus Celsius para Fahrenheit, e de quilmetrospra milhas.

    10

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    package main

    import ("fmt""os""strconv"

    )

    A primeira seo do programa no muito diferente do que j vimos an-teriormente. Porm, como este exemplo um pouco mais complexo, impor-tamos o pacote fmt, mas tambm precisamos dos pacotes os e strconv.

    O pacote os possui uma srie de operaes que lidam com o sistemaoperacional hospedeiro de forma independente, facilitando a escrita de apli-caes multiplataformas. No exemplo a seguir, utilizaremos este pacote parater acesso aos argumentos passados ao nosso programa via linha de comando,e tambm para instruir o programa a interromper sua execuo e retornar umcdigo de erro adequado em casos excepcionais.

    J o pacote strconv fornece uma grande variedade de funes para aconverso de strings para outros formatos e vice-versa. Os argumentosrecebidos via linha de comando so sempre tratados como strings, por-tanto, para que o nosso conversor seja capaz de realizar operaes matem-ticas sobre os valores, utilizaremos o pacote strconv para convert-los emnmeros decimais.

    A seguir precisamos declarar a funo main() que, conforme visto nocaptulo anterior, no recebe argumentos e no retorna nenhum valor. O con-tedo desta funo ser discutido passo a passo.

    func main() {// ...

    }

    Como em qualquer programa que depende da entrada de dados de usu-rios, a primeira coisa que o conversor faz garantir que os argumentos passa-dos via linha de comando esto em um formato minimamente so. Para isso,verificamos a quantidade de argumentos recebidos presentes em os.Args atravs do uso da funo embutida len().

    11

  • 2.4. Exemplo 1: conversor de medidas Casa do Cdigo

    os.Args contmuma lista (tecnicamente um slice) de todos os argumen-tos passados para o programa, sendo que por padro o primeiro argumentosempre ser o prprio nome do programa executado. Portanto, verificamosse os.Args possui pelomenos trs argumentos o nome do programa; pelomenos um valor a ser convertido; e a unidade referente aos valores atravsda instruo de controle if.

    Caso a quantidade de argumentos fornecidos seja menor do que trs, uti-lizamos a j conhecida funo Println do pacote fmt para apresentar aousurio uma mensagem de ajuda com o formato de entrada do programa,e logo depois utilizamos a funo os.Exit() para abortar a execuo doprograma. Note que a funo os.Exit() foi chamada com o valor 1 comoargumento; o valor especificado retornado como cdigo de erro para o sis-tema operacional. Seguindo os padres de programas Unix, qualquer valordiferente de 0 indica uma execuo anormal.

    if len(os.Args) < 3 {fmt.Println("Uso: conversor ")os.Exit(1)

    }

    Seguindo a execuo quando trs ou mais argumentos foram fornecidos,encontramos a declarao e atribuio de duas variveis: unidadeOrigeme valoresOrigem. Em Go, quando uma varivel declarada e atribuda namesma operao, no preciso informar seu tipo o compilador inteligenteo suficiente para adivinh-lo baseado na operao de atribuio; ou seja, casoo valor 10 seja atribudo varivel x, o compilador sabe que o tipo de x um nmero inteiro, ou int em Go.

    Atribumos unidadeOrigem o ltimo argumento passado, de acordocom o formato esperado. Para isso, acessamos a ltima posio do sliceos.Args. Slices so listas indexadas e de tamanho varivel em Go, sendoque o primeiro elemento possui ndice 0. Sendo assim, o ndice do ltimoelemento sempre n - 1, sendo n o nmero total de elementos presentesno slice no caso, len(os.Args)-1.

    unidadeOrigem := os.Args[len(os.Args)-1]

    12

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    A varivel valoresOrigem recebe uma sublista dos argumentos, des-cartando o primeiro (o nome do programa) e o ltimo (a unidade j atribuda unidadeOrigem). EmGo, este tipo de operao trivial e conhecida comoslicing (fatiar, separar) da vem o nome dado ao tipo de listas dinmicas.Assim, fatiamos a lista completa de argumentos, obtendo somente os valoresque devemos converter: os.Args[1 : len(os.Args)-1]. O ndice 1refere-se ao segundo elemento, e len(os.Args)-1 ao ltimo elemento, que desconsiderado no slice retornado.

    valoresOrigem := os.Args[1 : len(os.Args)-1]

    Uma vez que identificamos a unidade-origem e os valores a serem conver-tidos, precisamos descobrir qual a unidade-destino e, consequentemente,qual frmula de converso dever ser usada. Conforme mencionado anteri-ormente, nosso programa s sabe converter graus Celsius para Fahrenheit equilmetros para milhas.

    Declaramos a varivel unidadeDestino como sendo do tipo string.Como seu contedo depende de uma verificao e, portanto, no podemosatribuir a ela um valor no momento da declarao, precisamos utilizar apalavra-chave var antes do nome da varivel, e neste caso precisamos tam-bm informar seu tipo logo aps o nome. Se voc est acostumado com lin-guagens como C e Java, pode achar a ordem da declarao um pouco estra-nha nestas linguagens, declara-se primeiro o tipo e depois o nome da vari-vel. Repare como em Go a leitura da declarao fica mais fluida: declare umavarivel com nome unidadeDestino do tipo string.

    var unidadeDestino string

    Para atribuir o valor correto unidadeDestino, verificamos o con-tedo de unidadeOrigem: caso seja a string "celsius", atribumos"fahrenheit"; caso seja "quilometros", atribumos "milhas"; casoseja qualquer outro valor desconhecido, informamos este fato ao usurio e in-terrompemos a execuo do programa. Note que, desta vez, utilizamos a fun-o fmt.Printf, que imprimena sada padro uma stringde acordo como formato especificado, similar funo printf da linguagemC.Neste caso,especificamos o formato %s no uma unidade conhecida!, onde%s ser substitudo pelo valor da varivel unidadeDestino.

    13

  • 2.4. Exemplo 1: conversor de medidas Casa do Cdigo

    if unidadeOrigem == "celsius" {unidadeDestino = "fahrenheit"

    } else if unidadeOrigem == "quilometros" {unidadeDestino = "milhas"

    } else {fmt.Printf("%s no uma unidade conhecida!",

    unidadeDestino)os.Exit(1)

    }

    Agora que conhecemos a unidade-origem, os valores a serem conver-tidos e tambm a unidade-destino, vamos converso propriamente dita.Para converter todos os valores informados, precisamos percorrer o slicevaloresOrigem, transformar cada valor em um nmero decimal, aplicara frmula sobre este nmero e imprimir o resultado.

    Go possui apenas uma estrutura de repetio, for, que pode ser usadade diferentes formas de acordo com o contexto; no nosso caso, utilizamosfor em conjunto com o operador range para obter acesso a cada elementodo slice valoresOrigem. O operador range, quando aplicado a um slice,retorna dois valores para cada elemento: primeiro, o ndice do elemento noslice, e depois, o elemento propriamente dito. Atribumos o ndice variveli e o elemento v.

    for i, v := range valoresOrigem {// ...

    }

    Em seguida, utilizamos a funo ParseFloat() do pacote strconvpara converter a string em um nmero de ponto flutuante. Esta funorecebe dois argumentos o valor a ser convertido e a preciso do valor retor-nado (32 ou 64 bits) e retorna dois valores: o valor convertido e um erro deconverso (que nil quando o valor convertido com sucesso). Caso umerro acontea, informamos ao usurio (novamente utilizando fmt.Printfpara mostrar corretamente o valor string, representado como %s no for-mato e sua posio na lista de argumentos int, representado como %d)e interrompemos a execuo do programa.

    14

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    valorOrigem, err := strconv.ParseFloat(v, 64)if err != nil {

    fmt.Printf("O valor %s na posio %d no um nmero vlido!\n",v, i)

    os.Exit(1)}

    Com o valor correto em mos, declaramos a varivel valorDestinocomo sendo do tipo float64, verificamos qual a unidade-origem e, apli-cando a frmula correspondente unidade, atribumos a ela o valor conver-tido.

    var valorDestino float64

    if unidadeOrigem == "celsius" {valorDestino = valorOrigem*1.8 + 32

    } else {valorDestino = valorOrigem / 1.60934

    }

    Por fim, utilizamos novamente a funo fmt.Printf para apresentaro valor convertido e sua unidade ao usurio. Repare que utilizamos %.2fpara informar funo que o valor do tipo float e deve ser arredondadoe formatado com duas casas decimais.

    fmt.Printf("%.2f %s = %.2f %s\n",valorOrigem, unidadeOrigem, valorDestino, unidadeDestino)

    A seguir, voc encontra o cdigo completo do conversor. Para execut-lo,crie um novo diretrio chamado cap2-conversor em $GOPATH/src e,dentro dele, crie um arquivo chamado conversor.go com o contedo:

    package main

    import ("fmt""os""strconv"

    15

  • 2.4. Exemplo 1: conversor de medidas Casa do Cdigo

    )

    func main() {if len(os.Args) < 3 {

    fmt.Println("Uso: conversor ")os.Exit(1)

    }

    unidadeOrigem := os.Args[len(os.Args)-1]valoresOrigem := os.Args[1 : len(os.Args)-1]

    var unidadeDestino string

    if unidadeOrigem == "celsius" {unidadeDestino = "fahrenheit"

    } else if unidadeOrigem == "quilometros" {unidadeDestino = "milhas"

    } else {fmt.Printf("%s no uma unidade conhecida!",

    unidadeDestino)os.Exit(1)

    }

    for i, v := range valoresOrigem {valorOrigem, err := strconv.ParseFloat(v, 64)if err != nil {

    fmt.Printf("O valor %s na posio %d " +

    "no um nmero vlido!\n",v, i)

    os.Exit(1)}

    var valorDestino float64

    if unidadeOrigem == "celsius" {valorDestino = valorOrigem*1.8 + 32

    } else {valorDestino = valorOrigem / 1.60934

    16

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    }

    fmt.Printf("%.2f %s = %.2f %s\n",valorOrigem, unidadeOrigem,valorDestino, unidadeDestino)

    }}

    Um exemplo da execuo do programa:

    $ go run cap2-conversor/conversor.go 32 27.4 -3 0 celsius32.00 celsius = 89.60 fahrenheit27.40 celsius = 81.32 fahrenheit-3.00 celsius = 26.60 fahrenheit0.00 celsius = 32.00 fahrenheit

    Algumas das tcnicas apresentadas neste exemplo tambm sero utiliza-das nos prximos e, portanto, no sero explicadas novamente.

    2.5 Criando funes bsicasAntes de implementar o prximo exemplo, vejamos algumas formas bsicaspara a definio de funes:

    func imprimirDados(nome string, idade int) {fmt.Printf("%s tem %d anos.", nome, idade)

    }

    func main() {imprimirDados("Fernando", 29)

    }

    A funo imprimirDados() recebe dois argumentos, uma stringrepresentando o nome, e um int representando a idade, e imprime os doisargumentos atravs do uso da funo fmt.Printf(). Assim como a funoespecial main(), imprimirDados() no retorna nenhum valor.

    func soma(n, m int) int {return n + m

    17

  • 2.6. Exemplo 2: quicksort e funes Casa do Cdigo

    }

    func main() {s := soma(3, 5)fmt.Println("A soma ", s)

    }

    Esta outra funo simples, denominada soma(), recebe dois argumen-tos n e m do tipo int e retorna a soma dos dois, tambm do tipo int.Note que, como os dois argumentos so do mesmo tipo, podemos simplifi-car sua declarao separando os nomes dos argumentos com uma vrgula eespecificando seu tipo uma nica vez.

    Agora que conhecemos o bsico sobre funes, vamos implementaodo prximo exemplo.

    2.6 Exemplo 2: quicksort e funesO segundo exemplo uma implementao simples do famoso algoritmo deordenao quicksort. A ideia bsica deste algoritmo :

    1) eleger um elemento da lista como piv e remov-lo da lista;

    2) particionar a lista em duas listas distintas: uma contendo elementos me-nores que o piv, e outra contendo elementos maiores;

    3) ordenar as duas listas recursivamente;

    4) retornar a combinao da lista ordenada de elementos menores, o prpriopiv, e a lista ordenada de elementos maiores.

    Como a recursividade naturalmente parte deste algoritmo, nossa im-plementao ser baseada em uma funo recursiva. Este exemplo maiscomplexo do que o anterior, pormmuitomais expressivo, e demonstra comoalgumas construes da linguagem ajudam a escrever programas bastante su-cintos.

    18

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    Implementando o quicksort

    Nosso programa limita-se a ordenar nmeros inteiros. Os nmeros sorecebidos como argumentos via linha de comando de formamuito similar aoexemplo do conversor de unidades; a diferena que desta vez utilizamos afuno strconv.Atoi() para convert-los em nmeros inteiros em vez dedecimais.

    Repare tambm que a declarao do slice numeros, que armazenar osnmeros inteiros, bastante diferente da forma que foi utilizada no conver-sor: aqui utilizamos a funo nativa make() para criar e inicializar um slicedo tipo []int um slice de nmeros inteiros especificando tambm seutamanho inicial como sendo o mesmo da lista recebida como argumento, oque sempre uma boa ideia quando sabemos de antemo qual ser o tamanhofinal do slice.

    Depois de converter a entrada do programa para uma lista de nmeros in-teiros, chamamos uma funo denominada quicksort(), passando comoargumento a lista de nmeros a ser ordenada e imprimindo a lista resultante.

    func main() {entrada := os.Args[1:]numeros := make([]int, len(entrada))

    for i, n := range entrada {numero, err := strconv.Atoi(n)if err != nil {

    fmt.Printf("%s no um nmero vlido!\n", n)os.Exit(1)

    }numeros[i] = numero

    }

    fmt.Println(quicksort(numeros))}

    A funo quicksort() responsvel pela implementao do algo-ritmo. Ela recebe como argumento um slice de nmeros inteiros e retornaum slice ordenado, como podemos ver na assinatura a seguir:

    19

  • 2.6. Exemplo 2: quicksort e funes Casa do Cdigo

    func quicksort(numeros []int) []int {// ...

    }

    O primeiro passo verificar se a lista de entrada est vazia ou contmapenas um nmero e, em caso positivo, retornar a prpria lista. Esta condi-o extremamente importante em funes recursivas; a chamada condiode parada, que previne que a funo seja executada eternamente ou at queacontea uma sobrecarga na pilha de execuo (stack overflow).

    if len(numeros)

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    Isso pode parecer um tanto estranho quando queremos de fato removerum elemento do slice. Entretanto, combinando o uso append() com opera-es de slice, temos uma construo bastante poderosa e idiomtica em Go:

    n = append(n[:indicePivo], n[indicePivo+1:]...)

    Primeiro, fatiamos o slice n do primeiro elemento at o piv n[:indicePivo] e utilizamos este novo slice como base para a ope-rao de append(); depois, fatiamos novamente n, partindo do ele-mento imediatamente posterior ao piv at o ltimo elemento disponvel n[indicePivo+1:] e utilizamos este slice como valor a ser adicionadoao slice-base. muito importante notar o uso das reticncias ao final do se-gundo argumento: estamos informando que todos os elementos do segundoslice devem ser adicionados ao slice-base.

    O resultado desta operao uma lista que contm todos os elementosanteriores ao piv, e todos os elementos posteriores a ele, exceto o prpriopiv. Misso cumprida!

    O prximo passo do algoritmo particionar o slice de nmeros em doisnovos slices um contendo todos os elementos menores ou iguais ao piv, eoutro contendo somente os elementos maiores. Esta tarefa delegada a umaoutra funo chamada particionar(), que ser explicada em breve. Noteque tiramos proveito do fato de que a funo particionar() retorna doisvalores distintos dois slices:

    menores, maiores := particionar(n, pivo)

    O ltimo passo ordenar estes dois slices recursivamente e combinaros resultados com o piv, e exatamente o que a ltima linha da funoquicksort() faz novamente atravs do uso da funo append():

    return append(append(quicksort(menores), pivo),quicksort(maiores)...)

    Primeiro, chamamos quicksort() recursivamente para ordenar o slicemenores; depois adicionamos o pivo ao resultado desta ordenao; emseguida, fazemos outra chamada recursiva a quicksort() para ordenar

    21

  • 2.6. Exemplo 2: quicksort e funes Casa do Cdigo

    o slice maiores; e por fim, combinamos as duas listas ordenadas comappend() de maneira similar apresentada anteriormente e retornamos.

    A seguir a listagem completa da funo quicksort():

    func quicksort(numeros []int) []int {if len(numeros)

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    func particionar(numeros []int,pivo int) (menores []int,maiores []int) {

    for _, n := range numeros {if n

  • 2.6. Exemplo 2: quicksort e funes Casa do Cdigo

    entrada := os.Args[1:]numeros := make([]int, len(entrada))

    for i, n := range entrada {numero, err := strconv.Atoi(n)if err != nil {

    fmt.Printf("%s no um nmero vlido!\n", n)os.Exit(1)

    }numeros[i] = numero

    }

    fmt.Println(quicksort(numeros))}

    func quicksort(numeros []int) []int {if len(numeros)

  • Casa do Cdigo Captulo 2. Explorando a sintaxe bsica

    if n

  • Captulo 3

    Indo alm: mais exemplos

    No captulo anterior, aprendemos alguns elementos bsicos da sintaxe e algu-mas das construes mais fundamentais em Go. Neste captulo avanaremosum pouco mais atravs de exemplos que utilizam maps (dicionrios de da-dos) e tipos customizados.

    3.1 Exemplo 3: mapas e estatsticasUm map tambm conhecido como array associativo ou dicionrio umacoleo de pares chave/valor sem ordem definida, em que cada chave nicae armazena um nico valor.

    O exemplo a seguir utiliza um map para contar a frequncia de pa-lavras com as mesmas iniciais. Recebemos a lista de palavras via linhade comando, assim como j fizemos nos exemplos anteriores. Armaze-namos a lista em um slice denominado palavras, chamamos a funo

  • 3.1. Exemplo 3: mapas e estatsticas Casa do Cdigo

    colherEstatisticas() utilizando o slice palavras como argumento,armazenamos as estatsticas numa varivel e, por fim, chamamos a funoimprimir() para imprimi-las.

    package main

    import ("fmt""os""strings"

    )

    func main() {palavras := os.Args[1:]

    estatisticas := colherEstatisticas(palavras)

    imprimir(estatisticas)}

    Nenhumanovidade at aqui, exceto pelo fato de que importamos o pacotestrings. Este pacote contm uma gama de funes para manipulao destrings.

    A funo colherEstatisticas() possui a seguinte assinatura:

    func colherEstatisticas(palavras []string) map[string]int

    Repare que o tipo de retorno map[string]int: um map com chavesdo tipo string e valores do tipo int, ideal para armazenar as estatsticasque desejamos. Para cada item armazenado, a chave a letra inicial da palavrae o valor a quantidade de palavras com esta inicial.

    Dentro da funo declaramos o map que ir armazenar as estatsticas edamos a ele o nome estatisticas. Para inicializar um map utilizamos afuno make(), similar inicializao de slices:

    estatisticas := make(map[string]int)

    Em seguida, para cada palavra, extramos sua letra inicial palavra[0] e, utilizando a funo ToUpper() do pacote strings, convertemos a

    28

  • Casa do Cdigo Captulo 3. Indo alm: mais exemplos

    inicial para maiscula e a armazenamos na varivel inicial. Desta forma,garantimos que no haja distino de palavras escritas em caixa alta ou baixa.

    Com a inicial em mos, procuramos no mapa estatisticas uma en-trada cuja chave seja esta inicial:

    contador, encontrado := estatisticas[inicial]

    Para acessar um valor em um map, utilizamos uma notao similar aoacesso de valores em um slice, porm utilizamos a chave do valor entre oscolchetes no nosso caso, a string representando a letra inicial. Esta ope-rao retorna dois valores: o valor armazenado sob aquela chave e um boolindicando se a chave existe ou no no map. Atribumos estes dois valoress variveis contador e encontrado, respectivamente. Caso j exista umcontador para a inicial ( encontrado possui o valor true), incrementa-mos a contagem ( contador + 1) e a atualizamos no mapa; caso contrrio, a primeira ocorrncia de uma palavra com esta inicial e, portanto, armaze-namos a contagem inicial 1.

    Aps iterar sobre todas as palavras, nossomapa estatisticas possui acontagem completa, e ento ele usado como valor de retorno para a funocolherEstatisticas().

    if encontrado {estatisticas[inicial] = contador + 1

    } else {estatisticas[inicial] = 1

    }

    return estatisticas

    A funo imprimir() recebe o mapa contendo as estatsticas e sim-plesmente itera sobre todas as entradas, imprimindo as estatsticas para cadainicial:

    func imprimir(estatisticas map[string]int) {fmt.Println("Contagem de palavras iniciadas em cada letra:")

    for inicial, contador := range estatisticas {fmt.Printf("%s = %d\n", inicial, contador)

    29

  • 3.1. Exemplo 3: mapas e estatsticas Casa do Cdigo

    }}

    Veja como iterar sobre todas as entradas de um map uma tarefa trivialquando utilizamos o operador range: como cada entrada no mapa , pordefinio, um par chave, valor, o retorno de range se encaixa perfeitamente. importante ressaltar que, em Go, a ordem de iterao sobre maps alea-tria e, portanto, no se deve confiar nela. No captulo 4 estudaremos mapsmais a fundo e veremos como garantir a ordem desejada nestes casos.

    A seguir voc encontra a listagem completa do programa. Crie o arquivocap3-maps/maps.go com o seguinte contedo:

    package main

    import ("fmt""os""strings"

    )

    func main() {palavras := os.Args[1:]

    estatisticas := colherEstatisticas(palavras)

    imprimir(estatisticas)}

    func colherEstatisticas(palavras []string) map[string]int {estatisticas := make(map[string]int)

    for _, palavra := range palavras {inicial := strings.ToUpper(string(palavra[0]))contador, encontrado := estatisticas[inicial]if encontrado {

    estatisticas[inicial] = contador + 1} else {

    estatisticas[inicial] = 1}

    30

  • Casa do Cdigo Captulo 3. Indo alm: mais exemplos

    }

    return estatisticas}

    func imprimir(estatisticas map[string]int) {fmt.Println("Contagem de palavras iniciadas em cada letra:")

    for inicial, contador := range estatisticas {fmt.Printf("%s = %d\n", inicial, contador)

    }}

    E um exemplo da entrada e sada:

    $ go run cap3-maps/maps.go \Lorem ipsum dolor sit amet leo eu velit ante sagittis dolor \turpis disContagem de palavras iniciadas em cada letra:L = 2I = 1D = 3S = 2A = 2E = 1V = 1T = 1

    3.2 Exemplo 4: pilhas e tipos customizadosDefinir tipos customizados traz inmeros benefcios legibilidade e robus-tez de um programa. Go possui algumas funcionalidades que facilitam estatarefa, e veremos mais sobre este assunto no captulo 5.

    O exemplo a seguir apresenta uma implementao simples de uma pilhade objetos atravs de um tipo customizado.

    Desta vez no receberemos argumentos via linha de comando, simples-mente empilhamos e desempilhamos alguns valores e interagimos comapilhachamando alguns outros mtodos utilitrios.

    31

  • 3.2. Exemplo 4: pilhas e tipos customizados Casa do Cdigo

    Para facilitar o entendimento da interface da pilha, veremos cada passoda funo main() que interage com ela, e posteriormente, veremos a imple-mentao de cada mtodo.

    Inicialmente, criamos uma instncia da pilha e atribumos o objeto retor-nado varivel pilha. Em seguida, imprimimos o resultado da chamadade mtodos: pilha.Tamanho() e pilha.Vazia(), que retornam 0 etrue, respectivamente.

    pilha := Pilha{}

    fmt.Println("Pilha criada com tamanho ", pilha.Tamanho())fmt.Println("Vazia? ", pilha.Vazia())

    Se voc j programou em qualquer outra linguagem com suporte a ori-entao a objetos, no encontrou nenhuma grande novidade at aqui. En-tretanto, repare que os nomes de todos os mtodos que chamamos se ini-ciam com uma letra maiscula. Go possui uma forma muito simples de con-trole de acesso: todo identificador iniciado com uma letra maiscula den-tro de um pacote automaticamente exportado e visvel fora do pacote. Naprtica, j utilizamos esta funcionalidade quando chamamos funes comofmt.Println(), por exemplo.

    Uma pilha vazia no muito til. Vamos agora empilhar quatro objetos everificar o Tamanho() da pilha e se ela continua Vazia():

    pilha.Empilhar("Go")pilha.Empilhar(2009)pilha.Empilhar(3.14)pilha.Empilhar("Fim")

    fmt.Println("Tamanho aps empilhar 4 valores: ",pilha.Tamanho())

    fmt.Println("Vazia? ", pilha.Vazia())

    Veja que somos capazes de empilhar valores de tipos completamente di-ferentes: duas strings, um int e um float64. Sem nenhuma surpresa,as duas ltimas linhas do trecho anterior imprimiro 4 e false, respecti-vamente. E agora que temos uma pilha cheia, que tal desempilhar todos osobjetos?

    32

  • Casa do Cdigo Captulo 3. Indo alm: mais exemplos

    Como vimos anteriormente, for a nica estrutura de repetio dis-ponvel em Go. Mas isto no significa que ela seja limitada. A seguir uti-lizamos uma forma diferente da iterao com for, similar construowhile em outras linguagens. Enquanto a pilha no estiver vazia ( for!pilha.Vazia()), o bloco ser executado. Dentro do bloco, desempilha-mos um valor e o atribumos varivel v, cujo valor mostrado ao usurio,junto com os resultados de pilha.Tamanho() e pilha.Vazia() apscada operao de remoo. Note que o mtodo pilha.Desempilhar()retorna dois valores. O segundo valor indica um erro que, neste momento,optamos por ignorar, atribuindo-o ao identificador vazio _.

    for !pilha.Vazia() {v, _ := pilha.Desempilhar()fmt.Println("Desempilhando ", v)fmt.Println("Tamanho: ", pilha.Tamanho())fmt.Println("Vazia? ", pilha.Vazia())

    }

    Ao final da iterao, a pilha est vazia novamente. Poderamos nos certifi-car deste fato chamando a funo pilha.Vazia(). Porm, decidimos cha-mar o mtodo pilha.Desempilhar() mais uma vez. Agora, no entanto,ignoramos o primeiro valor retornado e atribumos o segundo varivel err.Por fim, se um erro foi retornado, ele apresentado ao usurio.

    _, err := pilha.Desempilhar()if err != nil {

    fmt.Println(err)}

    Agora vamos definio do tipo Pilha e seus mtodos.Diferente de outras linguagens com suporte programao orientada a

    objetos, Go no possui o conceito de classes. Em vez disso, definimos estru-turas de dados em forma de structs, construes semelhantes s structsda linguagem C, e um conjunto de funes mtodos, neste caso que ma-nipulam estes dados. A seguir temos a definio do novo tipo Pilha:

    type Pilha struct {valores []interface{}

    }

    33

  • 3.2. Exemplo 4: pilhas e tipos customizados Casa do Cdigo

    O novo tipo possui apenas ummembro: um slice denominado valores,que armazena objetos do tipo interface{}. Este tipo conhecido comointerface vazia e descreve uma interface sem nenhummtodo. Qualquer tipoem Go implementa pelo menos zero mtodos, portanto satisfaz a interfacevazia. Na prtica, isto faz com que a nossa implementao de pilha seja capazde armazenar objetos de qualquer tipo Go vlido, conforme pudemos ver naimplementao da funo main().

    O slice valores foi intencionalmente declarado coma inicialminscula,garantindo que ele no seja acessvel em outro pacote.

    Tendo definido o tipo Pilha, podemos comear a definir seus mtodos.Comearemos com o simples mtodo Tamanho():

    func (pilha Pilha) Tamanho() int {return len(pilha.valores)

    }

    Adefinio de ummtodo muito semelhante definio de uma funo,como j vimos anteriormente. A diferena marcante que mtodos definemum objeto receptor, que neste caso foi chamado de pilha e do tipo Pilha,que deve ser especificado entre parnteses antes do nome do mtodo. Assim,o mtodo Tamanho() acessa o slice pilha.valores e retorna seu tama-nho utilizando a funo len().

    importante observar que, como Go no possui o conceito de classes,seusmtodos tambmno possuemum receptor implcito conhecido comoself ou this em outras linguagens. Isto facilita muito a implementaodo runtime da linguagem e aumenta a clareza do cdigo, j que dentro dadefinio do mtodo s existe uma forma de acessar seu receptor.

    O mtodo pilha.Vazia() trivial e simplesmente verifica se o tama-nho atual da pilha igual a zero:

    func (pilha Pilha) Vazia() bool {return pilha.Tamanho() == 0

    }

    Os dois mtodos apresentados at aqui so imutveis no possuem ne-nhum efeito colateral e, portanto, bastante simples.

    34

  • Casa do Cdigo Captulo 3. Indo alm: mais exemplos

    No caso dos mtodos Empilhar() e Desempilhar(), desejamos al-terar a pilha na qual tais mtodos foram chamados. Em Go, argumentos defunes e mtodos so sempre passados por cpia (com a exceo de slices,maps e channels, que so conhecidos como reference types, como veremosnos prximos captulos). Por isso, quando precisamos alterar qualquer argu-mento incluindo receptores de mtodos devemos declar-los como pon-teiros. Vejamos a definio do mtodo Empilhar():

    func (pilha *Pilha) Empilhar(valor interface{}) {pilha.valores = append(pilha.valores, valor)

    }

    Repare que o tipo do receptor foi definido como *Pilha, e indica quea varivel pilha um ponteiro para um objeto do tipo Pilha. Para em-pilhar um novo objeto, adicionamo-lo ao slice pilha.valores atravs douso da funo append() e alteramos o valor atual de pilha.valores paraguardar o novo slice que contm o objeto adicionado.

    De forma similar, o mtodo Desempilhar() tambm define o receptorcomo sendo um ponteiro. Como vimos na definio da funo main(), estemtodo possui dois valores de retorno: o objeto desempilhado e um valor dotipo error que retornado quando a pilha est vazia. Para isso, utilizamos omtodo pilha.Vazia() e, em caso de retorno positivo, criamos um novoerro atravs da funo errors.New() e retornamos nil no lugar do objetodesempilhado, junto com o erro recm-criado.

    Caso a pilha no esteja vazia, atribumos o ltimo objeto empilhado varivel valor. Em seguida, atualizamos o slice pilha.valores com umafatia do slice atual, incluindo todos os objetos empilhados com a exceo doltimo que acabou de ser desempilhado. Finalmente, retornamos o objetoremovido e nil no lugar do erro, indicando que o objeto foi desempilhadocom sucesso.

    func (pilha *Pilha) Desempilhar() (interface{}, error) {if pilha.Vazia() {

    return nil, errors.New("Pilha vazia!")}valor := pilha.valores[pilha.Tamanho()-1]

    35

  • 3.2. Exemplo 4: pilhas e tipos customizados Casa do Cdigo

    pilha.valores = pilha.valores[:pilha.Tamanho()-1]return valor, nil

    }

    Confira a listagem completa da implementao da pilha. Utilize o con-tedo a seguir para criar o arquivo cap3-pilha/pilha.go:

    package main

    import ("errors""fmt"

    )

    func main() {pilha := Pilha{}fmt.Println("Pilha criada com tamanho ", pilha.Tamanho())fmt.Println("Vazia? ", pilha.Vazia())

    pilha.Empilhar("Go")pilha.Empilhar(2009)pilha.Empilhar(3.14)pilha.Empilhar("Fim")fmt.Println("Tamanho aps empilhar 4 valores: ",

    pilha.Tamanho())fmt.Println("Vazia? ", pilha.Vazia())

    for !pilha.Vazia() {v, _ := pilha.Desempilhar()fmt.Println("Desempilhando ", v)fmt.Println("Tamanho: ", pilha.Tamanho())fmt.Println("Vazia? ", pilha.Vazia())

    }

    _, err := pilha.Desempilhar()if err != nil {

    fmt.Println("Erro: ", err)}

    }

    36

  • Casa do Cdigo Captulo 3. Indo alm: mais exemplos

    type Pilha struct {valores []interface{}

    }

    func (pilha Pilha) Tamanho() int {return len(pilha.valores)

    }

    func (pilha Pilha) Vazia() bool {return pilha.Tamanho() == 0

    }

    func (pilha *Pilha) Empilhar(valor interface{}) {pilha.valores = append(pilha.valores, valor)

    }

    func (pilha *Pilha) Desempilhar() (interface{}, error) {if pilha.Vazia() {

    return nil, errors.New("Pilha vazia!")}valor := pilha.valores[pilha.Tamanho()-1]pilha.valores = pilha.valores[:pilha.Tamanho()-1]return valor, nil

    }

    Um exemplo deste programa em execuo:

    $ go run cap3-pilha/pilha.goPilha criada com tamanho 0Vazia? trueTamanho aps empilhar 4 valores: 4Vazia? falseDesempilhando FimTamanho: 3Vazia? falseDesempilhando 3.14Tamanho: 2Vazia? falseDesempilhando 2009

    37

  • 3.2. Exemplo 4: pilhas e tipos customizados Casa do Cdigo

    Tamanho: 1Vazia? falseDesempilhando GoTamanho: 0Vazia? trueErro: Pilha vazia!

    Neste captulo, melhoramos nosso conhecimento a respeito dos recursosbsicos da linguagem Go. Vimos a utilizao bsica de mapas para classifi-car valores sob chaves, e aprendemos a definir tipos customizados para criarabstraes mais complexas.

    Mapas sero discutidos emdetalhes no captulo 4.3, e aprenderemos aindamais sobre outras formas de criar tipos customizados no captulo 5.

    38

  • Captulo 4

    Colees: arrays, slices e maps

    Nos captulos anteriores, vimos como colees so essenciais para a soluode vrios problemas. Agora veremos as colees disponveis em Go em mai-ores detalhes.

    4.1 ArraysArrays emGo so colees indexadas de valores domesmo tipo e de tamanhofixo e invarivel. O primeiro elemento do array possui ndice 0, e o ltimoelemento sempre len(array) - 1.

    Para declarar um array, podemos utilizar uma das seguintes formas:

    var a [3]intnumeros := [5]int{1, 2, 3, 4, 5}primos := [...]int{2, 3, 5, 7, 11, 13}

  • 4.1. Arrays Casa do Cdigo

    nomes := [2]string{}

    fmt.Println(a, numeros, primos, nomes)

    O resultado da ltima linha seria o seguinte:

    [0 0 0] [1 2 3 4 5] [2 3 5 7 11 13] [ ]

    O array a foi declarado como [3]int, ou uma lista de 3 nmeros intei-ros. O tamanho de um array deve sempre ser especificado na declarao e fazparte do tipo do array [3]int e [5]int so considerados tipos diferentes,ainda que ambos carreguem valores do mesmo tipo.

    Apesar de no termos inserido nenhum valor em a, obtivemos [0 00] quando imprimimos seu contedo. Quando um array declarado, seuselementos ganham automaticamente um valor inicial conhecido como zerovalue em Go. Este valor inicial varia de acordo com o tipo de dados definidopara o array, da seguinte forma:

    false para valores do tipo bool;

    0 para ints;

    0.0 para floats;

    "" (ou string vazia) para strings;

    nil para ponteiros, funes, interfaces, slices, maps e channels.

    Algumas vezes sabemos de antemo quais sero os valores contidos numarray, e podemos declar-los utilizando a forma literal, como em numeros:= [5]int{1, 2, 3, 4, 5}. Nestes casos, para facilitar a vida do pro-gramador, pode-se substituir o tamanhodo array na declarao pelo operadorellipsis (reticncias), instruindo o compilador a calcular o tamanho do arraycom base na quantidade de elementos declarados, como fizemos em primos:= [...]int{2, 3, 5, 7, 11, 13}.

    Por fim, declaramos nomes := [2]string{} um array de duasstrings utilizando a forma literal, porm sem nenhum valor declaradoentre as chaves, fazendo com que os elementos sejam inicializados com "".

    40

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    Esta declarao idntica primeira forma utilizada (para declarar o arraya) e poderia ser escrita tambm da seguinte forma:

    var nomes [2]string

    O tamanho de um array pode ser obtido atravs do uso da funo len().Por exemplo, a execuo do cdigo a seguir nos daria 3 5 6 2 como resul-tado:

    fmt.Println(len(a), len(numeros), len(primos), len(nomes))

    Podemos tambm criar arrays cujos valores so tambm arrays, ou arraysmultidimensionais, de forma similar a arrays simples:

    var multiA [2][2]intmultiA[0][0], multiA[0][1] = 3, 5multiA[1][0], multiA[1][1] = 7, -2

    multiB := [2][2]int{{2, 13}, {-1, 6}}

    fmt.Println("Multi A:", multiA)fmt.Println("Multi B:", multiB)

    Repare que, na declarao literal do array multiB, no precisamos espe-cificar o tipo dos arrays internos.

    Imprimindo os valores de multiA e multiB teramos o seguinte resul-tado:

    Multi A: [[3 5] [7 -2]]Multi B: [[2 13] [-1 6]]

    Os arrays tm sua importncia e seu papel, mas no so muito flexveis,especialmente nos casos em que precisamos aumentar seu tamanho dinami-camente. possvel fazer isso com arrays, mas exige um grande trabalho ma-nual de verificao de limites, alocao de novos arrays e cpia de contedo.

    Em quase todos os casos, em Go mais comum utilizar slices. A prpriabiblioteca padro da linguagem utiliza slices em vez de arrays como parme-tros e tipos de retorno em sua API pblica. A seguir veremos os benefcios efacilidades de se trabalhar com eles.

    41

  • 4.2. Slices Casa do Cdigo

    4.2 SlicesUm slice uma poderosa abstrao criada em cima de arrays que introduzuma srie de facilidades. Diferente de um array, no entanto, um slice possuitamanho varivel e pode crescer indefinidamente.

    Na prtica, a utilizao de slices muito similar ao que acabamos deaprender sobre arrays. Para declarar um slice, podemos utilizar quase amesma sintaxe da declarao de arrays, incluindo a forma literal, com a dife-rena de que no especificamos o tamanho do slice:

    var a []intprimos := []int{2, 3, 5, 7, 11, 13}nomes := []string{}

    fmt.Println(a, primos, nomes)

    Teramos a seguinte sada impressa:

    [] [2 3 5 7 11 13] []

    Como no especificamos o tamanho dos slices a e nomes nem adicio-namos nenhum valor a eles durante a declarao, ambos esto vazios aps ainicializao.

    Arrays e slices em Go possuem duas propriedades importantes: tamanhoe capacidade len() e cap(), respectivamente, so as funes utilizadaspara inspecionar estas propriedades.

    Um slice pode ser criado tambm atravs da funo make(), que in-ternamente aloca um array e retorna uma referncia para o slice criado. Elapossui a seguinte assinatura:

    func make([]T, len, cap) []T

    T representa o tipo dos elementos do slice, len o tamanho inicial doarray alocado e cap o tamanho total da rea de memria reservada para ocrescimento do slice. Por convenincia, pode-se omitir o ltimo argumento,e neste caso Go assume por padro o mesmo valor do tamanho. Vejamosalguns exemplos:

    42

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    b := make([]int, 10)fmt.Println(b, len(b), cap(b))

    c := make([]int, 10, 20)fmt.Println(c, len(c), cap(c))

    Esse cdigo imprimiria:

    [0 0 0 0 0 0 0 0 0 0] 10 10[0 0 0 0 0 0 0 0 0 0] 10 20

    Note que os 10 primeiros elementos 10 foi o tamanho especificado dos dois slices foram inicializados com 0, o zero value para ints, conformevimos anteriormente.

    Uma grande vantagem de utilizar slices de fato, qualquer tipo criadoatravs da funo make() em vez de arrays que, quando usados comoargumentos ou no retorno de funes, so passados por referncia e no porcpia. Isto torna estas chamadas muito mais eficientes, pois o tamanho dareferncia ser sempre o mesmo, independente do tamanho do slice.

    Iteradores

    J iteramos sobre slices nos exemplos anteriores, e agora veremos todasas diferentes formas de iterao disponveis em Go.

    Sabemos que a nica estrutura de repetio em Go o for. Em suaforma mais bsica, podemos especificar uma condio lgica e o bloco serexecutado enquanto a condio for verdadeira similar construo whileem outras linguagens. Por exemplo:

    a, b := 0, 10

    for a < b {a += 1

    }

    fmt.Println(a)

    Podemos ler esse cdigo como enquanto a for menor que b, incremente

    43

  • 4.2. Slices Casa do Cdigo

    o valor de a. Seu resultado seria a impresso do valor 10, o valor de a apsa execuo do bloco for.

    Tambm podemos utilizar o for com uma clusula de inicializao, umacondio lgica e uma clusula de incremento a construo tradicional dalinguagem C:

    for i := 0; i < 10; i++ {// ...

    }

    importante notar que, como a varivel i no existia antes do for, seuescopo limitado ao bloco; se tentarmos acessar seu valor aps a execuo dobloco, teremos um erro de compilao informando que i no foi definida.Para resolver esses casos, precisamos declarar a varivel antes do for:

    var i int

    for i = 0; i < 10; i++ {// ...

    }

    fmt.Println(i)

    Desta forma, i continua existindo aps a execuo do bloco for, e oprograma imprimiria o valor 10.

    Qualquer elemento da clusula for pode ser omitido, mas o ponto evrgula obrigatrio a no ser que o nico elemento presente seja a condiolgica, como j vimos no primeiro exemplo de uso do for. Desconsiderandoo escopo da varivel de controle, todas as formas a seguir so equivalentes:

    i := 0for i < 10 {

    i += 1}

    for j := 0; j < 10; j++ {// ...

    }

    44

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    var k intfor k = 0; k < 10; {

    k += 1}

    l := 0for ; l < 10; l++ {

    // ...}

    A forma mais comum de iterar sobre slices, porm, utilizando o opera-dor range, como j vimos nos captulos anteriores. A sintaxe geral :

    for indice, valor := range slice {// ...

    }

    O operador range retorna o ndice de cada elemento, comeando em 0,e uma cpia de cada valor presente no slice. Quando precisamosmodificar osvalores em um slice, ou estamos interessados somente nos ndices, podemossimplesmente omitir o segundo valor na atribuio e acessar cada elementoatravs de seu ndice:

    numeros := []int{1, 2, 3, 4, 5}

    for i := range numeros {numeros[i] *= 2

    }

    fmt.Println(numeros)

    O cdigo anterior itera sobre um slice chamado numeros, multiplicandocada valor por 2. Aps a iterao, imprimimos o contedo de numeros eobtemos [2 4 6 8 10] como resultado.

    Ao contrrio, quando no precisamos dos ndices dos valores, podemosignor-los atribuindo-os ao identificador vazio:

    45

  • 4.2. Slices Casa do Cdigo

    for _, elemento := range slice {// ...

    }

    A ltima forma de utilizao do for a forma conhecida como loop in-finito, que em Go simplesmente uma clusula for sem nenhuma condio Go assume true como valor padro para a condio:

    for {// loop infinito

    }

    Para sair da execuo de um loop infinito, podemos utilizar o comandobreak.

    A seguir, temos um exemplo que inicia um loop infinito, gera nmerosaleatrios e sai do loop somente quando o nmero gerado for divisvel por42, ou seja, o resto da diviso do valor por 42 0 i%42 == 0. Crie umnovo arquivo chamado cap4-loop-infinito/loop_infinito.go como seguinte contedo:

    package main

    import ("fmt""math/rand""time"

    )

    func main() {rand.Seed(time.Now().UnixNano())n := 0

    for {n++

    i := rand.Intn(4200)fmt.Println(i)

    if i%42 == 0 {

    46

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    break}

    }

    fmt.Printf("Sada aps %d iteraes.\n", n)}

    Quando geramos nmeros aleatrios, sempre importante configurar ovalor conhecido como seed (semente) do gerador. No exemplo anterior, uti-lizamos o timestamp atual no formato padro do Unix o nmero de nanosegundos desde 1 de janeiro de 1970 para garantir que, a cada execuo doprograma, o gerador de nmeros aleatrios produza nmeros diferentes davez anterior.

    Um exemplo da sada do programa:

    $ go run cap4-loop-infinito/loop_infinito.go14583816241311795941562932911113130082235391029281470Sada aps 14 iteraes.

    Por padro, break sai do loopmais prximo ao ponto emque o comandofoi executado. H casos nos quais temos loops for aninhados e desejamosquebrar o loop externo em vez do interno. Para resolver este problema, Gotambm d suporte a blocos for nomeados. Vejamos um exemplo:

    var i int

    47

  • 4.2. Slices Casa do Cdigo

    externo:for {

    for i = 0; i < 10; i++ {if i == 5 {

    break externo}

    }}

    fmt.Println(i)

    Este recurso tambm importante quando temos, por exemplo, umbloco switch dentro de um bloco for. Como o comando breaktambm usado para sair do switch, precisamos especificar o nomedado ao bloco for se quisermos sair do loop. Vamos criar um programapara demonstrar o uso de laos nomeados. Crie um arquivo chamadocap4-loop-nomeado/loop_nomeado.go definindo um pacote main,importando o pacote fmt, e adicione o seguinte contedo funo main():

    var i int

    loop:for i = 0; i < 10; i++ {

    fmt.Printf("for i = %d\n", i)

    switch i {case 2, 3:

    fmt.Printf("Quebrando switch, i == %d.\n", i)break

    case 5:fmt.Println("Quebrando loop, i == 5.")break loop

    }}

    fmt.Println("Fim.")

    A sada do programa seria a seguinte:

    48

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    $ go run cap4-loop-nomeado/loop_nomeado.gofor i = 0for i = 1for i = 2Quebrando switch, i == 2.for i = 3Quebrando switch, i == 3.for i = 4for i = 5Quebrando loop, i == 5.Fim.

    Fatiando slices

    Fatiar (do ingls to slice) o nome dado a operaes que extraem partesde um slice ou de um array.

    Para fatiar um slice ou array, utilizamos a seguinte forma:

    novoSlice := slice[inicio : fim]

    Sendo que 0

  • 4.2. Slices Casa do Cdigo

    Como citado anteriormente neste captulo, um slice uma abstrao cri-ada com base nos arrays. Entender a relao entre eles essencial para traba-lhar efetivamente com slices.

    Sabemos que, quando um slice criado, um array alocado internamente.Quando fatiamos este slice e o atribumos a uma nova varivel, temos umnovo slice que compartilha o mesmo array interno do original. Isto querdizer que, quando um elemento comum aos dois slices modificado, estamodificao refletida no outro. Vejamos um exemplo:

    original := []int{1, 2, 3, 4, 5}fmt.Println("Original:", original)

    novo := original[1:3]fmt.Println("Novo:", novo)

    original[2] = 13

    fmt.Println("Original ps modificao:", original)fmt.Println("Novo ps modificao:", novo)

    A sada deste programa seria a seguinte:

    Original: [1 2 3 4 5]Novo: [2 3]Original ps modificao: [1 2 13 4 5]Novo ps modificao: [2 13]

    Veja que, ao alterarmos o valor de original[2] para 13, o contedo dosdois slices foi modificado. Isto verdade para qualquer slice criado fatiandoum outro slice, independente da quantidade de indirees criadas. Veja maisum exemplo:

    a := []string{"Paulo", "almoa", "em", "casa", "diariamente."}b := a[:len(a)-1]c := b[:len(b)-1]d := c[:len(c)-1]e := d[:len(d)-1]

    e[0] = "Tiago"fmt.Printf("%v\n%v\n%v\n%v\n%v\n", a, b, c, d, e)

    50

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    E o resultado impresso seria:

    [Tiago almoa em casa diariamente.][Tiago almoa em casa][Tiago almoa em][Tiago almoa][Tiago]

    Todas as operaes apresentadas anteriormente tambm so vlidas emum array, e importante mencionar que fatiar um array sempre resulta emum slice, nunca em outro array.

    Inserindo valores

    Todas as operaes realizadas sobre slices so baseadas na funoappend(). J vimos alguns exemplos de como us-la. Sua assinatura :

    func append(slice []Tipo, elementos ...Tipo) []Tipo

    Para inserir um novo valor ao final de um slice, utilizamos append()em sua forma mais bsica:

    s := make([]int, 0)s = append(s, 23)

    fmt.Println(s)

    Executando este cdigo, veramos o valor [23] impresso.Para inserir um novo valor no comeo de um slice, precisamos inicial-

    mente criar um novo slice contendo o valor que desejamos inserir, e depoisadicionar todos os elementos do slice inicial ao recm-criado. Parece compli-cado, mas na prtica bastante simples:

    s := []int{23, 24, 25}n := []int{22}s = append(n, s...)

    fmt.Println(s)

    51

  • 4.2. Slices Casa do Cdigo

    Uma outra opo mais sucinta criar o novo slice utilizando a forma li-teral na prpria chamada funo append():

    s := []int{23, 24, 25}s = append([]int{22}, s...)

    fmt.Println(s)

    As duas formas so semanticamente equivalentes e imprimiriam [22 2324 25]. Repare que utilizamos novamente o operador ellipsis (reticncias)para expandir o contedo do slice, garantindo que todos os valores sejampassados de forma individual funo append().

    Tambm possvel inserir um ou mais valores em qualquer posio nomeio de um slice. Para isso, precisamos fatiar o slice at a posio onde dese-jamos inserir os novos valores s[:3] e utilizar esta fatia do slice originalcomo primeiro argumento na chamada append(). O segundo argumentodeve ser, por sua vez, o resultado de uma segunda operao de append(),onde inserimos os valores restantes do slice original s[3:]... ao sliceque contm os novos valores append(v, s[3:]...).... Repare nova-mente o uso do operador ellipsis, aparecendo duas vezes neste exemplo:

    s := []int{11, 12, 13, 16, 17, 18}v := []int{14, 15}s = append(s[:3], append(v, s[3:]...)...)

    fmt.Println(s)

    Esse cdigo imprimiria [11 12 13 14 15 16 17 18].

    Removendo valores

    Para remover valores do comeo de um slice no precisamos da funoappend(). Basta fatiar o slice ignorando os ndices dos elementos que dese-jamos remover, e atribuir o novo slice mesma varivel. Por exemplo, pode-mos remover o primeiro valor de um slice da seguinte forma:

    s := []int{20, 30, 40, 50, 60}s = s[1:]

    52

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    fmt.Println(s)

    Assim, esse cdigo imprimiria o slice [30 40 50 60].De maneira anloga, para remover valores do final de um slice, fatiamos

    este slice ignorando os ndices dos elementos finais:

    s := []int{20, 30, 40, 50, 60}s = s[:3]

    fmt.Println(s)

    E agora o slice s teria o contedo [20 30 40].Por fim, para remover valores do meio de um slice, precisamos recorrer

    novamente funo append(), utilizando duas fatias do slice original comoargumentos a primeira incluindo elementos do incio at o ndice desejado,e a segunda comeando do prximo elemento que nos interessa. Por exem-plo, dado um slice s := []int{10, 20, 30, 40, 50, 60}, podemosremover os valores 30 e 40 da forma a seguir:

    s := []int{10, 20, 30, 40, 50, 60}s = append(s[:2], s[4:]...)

    fmt.Println(s)

    Ao executar esse cdigo, teramos o slice [10 20 50 60] impressoconforme o esperado.

    Copiando slices

    At agora, todas as vezes em quemanipulamos slices, utilizamos a funoappend() para modificar o slice original. No entanto, muitas vezes preci-samos manter o estado do slice original intacto e manipular uma cpia dele.Para isso, Go tambm possui uma funo embutida chamada copy(), coma seguinte assinatura:

    func copy(destino, origem []Tipo) int

    53

  • 4.3. Maps Casa do Cdigo

    Para criar uma cpia de um slice, chamamos a funo copy() da se-guinte forma:

    numeros := []int{1, 2, 3, 4, 5}dobros := make([]int, len(numeros))

    copy(dobros, numeros)

    for i := range dobros {dobros[i] *= 2

    }

    fmt.Println(numeros)fmt.Println(dobros)

    Imprimindo o contedo dos dois slices, teramos [1 2 3 4 5] e [2 46 8 10] como resultado, provando que o slice original no sofreu nenhumaalterao.

    4.3 MapsUm map, ou mapa, uma coleo de pares chave-valor sem nenhuma or-dem definida. a implementao emGo de uma estrutura de dados tambmconhecida como hashtable, dicionrio de dados ou array associativo, entreoutros nomes.

    Qualquer tipo que suporte os operadores de igualdade (i.e. == e !=) podeser usado como chave em um mapa. No entanto, as chaves em um mesmomapa devem necessariamente ser do mesmo tipo. importante mencionartambm que as chaves so nicas se armazenarmos dois valores distintossob uma mesma chave, o primeiro valor ser sobrescrito pelo segundo.

    Os valores em ummapa tambm devem sempre ser do mesmo tipo, em-bora a interface vazia interface{} seja um tipo vlido neste caso. Isto pos-sibilita armazenar virtualmente qualquer valor sob uma chave, porm requeruma assero de tipo (type assertion) quando os valores so recuperados.

    Podemos declarar mapas utilizando a forma literal ou a funo make(),de maneira muito semelhante declarao de slices. Por exemplo, para de-

    54

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    clarar um mapa vazio com chaves do tipo int e valores do tipo string, asduas opes a seguir so equivalentes:

    vazio1 := map[int]string{}vazio2 := make(map[int]string)

    A quantidade de valores armazenados em ummapa flexvel e pode cres-cer indefinidamente durante a execuo de um programa, sendo que pode-mos especificar sua capacidade inicial quando sabemos de antemo quantosvalores precisaremos armazenar. Isso pode ser importante quando sabemosque o mapa ir armazenar muitos valores, tornando o uso de memria maiseficiente e evitando problemas de performance. recomendvel especific-lasempre que possvel, e podemos faz-lo simplesmente passando um segundoargumento funo make():

    mapaGrande := make(map[int]string, 4096)

    A qualquer momento podemos inspecionar a quantidade de elementosque um mapa possui atravs da funo len(). Por exemplo:

    capitais := map[string]string{"GO": "Goinia","PB": "Joo Pessoa","PR": "Curitiba"}

    fmt.Println(len(capitais))

    O cdigo imprimiria o valor 3.

    Populando mapas

    Podemos popular ummapa utilizando literais nomomento da declaraoe/ou atribuindo valores individualmente aps a declarao:

    capitais := map[string]string{"GO": "Goinia","PB": "Joo Pessoa","PR": "Curitiba"}

    55

  • 4.3. Maps Casa do Cdigo

    capitais["RN"] = "Natal"capitais["AM"] = "Manaus"capitais["SE"] = "Aracaju"

    fmt.Println(capitais)

    populacao := make(map[string]int, 6)populacao["GO"] = 6434052populacao["PB"] = 3914418populacao["PR"] = 10997462populacao["RN"] = 3373960populacao["AM"] = 3807923populacao["SE"] = 2228489

    fmt.Println(populacao)

    O resultado da execuo desse cdigo seria o seguinte:

    map[GO:Goinia PB:Joo Pessoa PR:Curitiba RN:NatalAM:Manaus SE:Aracaju]

    map[GO:6434052 PB:3914418 PR:10997462 RN:3373960AM:3807923 SE:2228489]

    Para tornar o exemplo anterior um pouco mais real, vamos implement-lo utilizando um novo tipo, chamado Estado. Crie um arquivo chamadocap4-estados/estados.go e adicione a definio do tipo Estado coma seguinte estrutura:

    type Estado struct {nome stringpopulacao intcapital string

    }

    Agora, na funo main(), podemos popular um mapa de estados destaforma:

    estados := make(map[string]Estado, 6)

    estados["GO"] = Estado{"Gois", 6434052, "Goinia"}

    56

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    estados["PB"] = Estado{"Paraba", 3914418, "Joo Pessoa"}estados["PR"] = Estado{"Paran", 10997462, "Curitiba"}estados["RN"] = Estado{"Rio Grande do Norte", 3373960, "Natal"}estados["AM"] = Estado{"Amazonas", 3807923, "Manaus"}estados["SE"] = Estado{"Sergipe", 2228489, "Aracaju"}

    fmt.Println(estados)

    Esse cdigo imprimiria o seguinte contedo (as quebras de linha e algunsespaos em branco foram deliberadamente adicionados para facilitar a visu-alizao):

    $ go run cap4-estados/estados.gomap[GO:{Gois 6434052 Goinia}

    PB:{Paraba 3914418 Joo Pessoa}PR:{Paran 10997462 Curitiba}RN:{Rio Grande do Norte 3373960 Natal}AM:{Amazonas 3807923 Manaus}SE:{Sergipe 2228489 Aracaju}]

    Lookup: recuperando valores

    A operao de recuperao de um valor em um mapa conhecida comolookup e muito similar recuperao de valores de um ndice especfico emum slice basta especificar a chave desejada entre colchetes:

    sergipe := estados["SE"]

    fmt.Println(sergipe)

    Desta forma, a varivel sergipe receberia o Estado presente no mapae o valor {Sergipe 2228489 Aracaju} seria impresso.

    E o que aconteceria se tentssemos acessar o valor de um estado que noest presente no mapa?

    fmt.Println(estados["SP"])

    O valor { 0 } seria impresso. Note que existe um espao em brancoantes e outro depois do valor 0. Quando tentamos recuperar o valor de uma

    57

  • 4.3. Maps Casa do Cdigo

    chave que no est presente no mapa, o zero value do tipo armazenado re-tornado. Muitas vezes isso pode causar comportamentos inesperados em umprograma. Para evitar tais problemas, podemos testar se uma chave existe ouno:

    saoPaulo, encontrado := estados["SP"]if encontrado {

    fmt.Println(saoPaulo)}

    O segundo valor retornado pela operao de lookup um bool que re-ceber o valor true caso a chave esteja presente no mapa, ou false casocontrrio. Algumas vezes precisamos testar se uma dada chave existe, masno necessariamente precisamos do valor correspondente. Neste caso, pode-mos ignorar o valor atribuindo-o ao identificador vazio:

    _, encontrado := estados["RJ"]if encontrado {

    // ...}

    Atualizando valores

    Para atualizar valores existentes emummapa, utilizamos amesma sintaxeda insero de um novo valor. Porm, como as chaves so nicas, o valorarmazenado ser atualizado. Por exemplo:

    idades := map[string]int{"Joo": 37,"Ricardo": 26,"Joaquim": 41,

    }

    idades["Joaquim"] = 42

    fmt.Println(idades["Joaquim"])

    O valor 42 seria impresso.

    58

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    Removendo valores

    Podemos remover valores presentes emummapa utilizando a funo em-butida delete(), que possui a seguinte assinatura:

    func delete(m map[TipoChave]TipoValor, chave TipoChave)

    Por exemplo, caso desejssemos remover os dados do Estado do Ama-zonas no mapa de estados utilizado anteriormente, poderamos faz-lo destaforma:

    delete(estados, "AM")

    Iterando sobre mapas

    Podemos utilizar o operador range para iterar sobre todas as entradasde um mapa:

    for sigla, estado := range estados {fmt.Printf("%s (%s) possui %d habitantes.\n",

    estado.nome, sigla, estado.populacao)}

    Teramos a sada:

    Gois (GO) possui 6434052 habitantes.Paraba (PB) possui 3914418 habitantes.Paran (PR) possui 10997462 habitantes.Rio Grande do Norte (RN) possui 3373960 habitantes.Amazonas (AM) possui 3807923 habitantes.Sergipe (SE) possui 2228489 habitantes.

    Umdetalhe importante que j foi mencionado que a ordem dos elemen-tos em ummapa no garantida. Emmapas pequenos como os que apresen-tamos at aqui, a ordem de insero parece ter sido mantida, mas se tivermosum mapa com uma quantidade maior de elementos, veremos que isto no sempre verdade. Por exemplo:

    quadrados := make(map[int]int, 15)

    59

  • 4.3. Maps Casa do Cdigo

    for i := 1; i

  • Casa do Cdigo Captulo 4. Colees: arrays, slices e maps

    "sort")

    func main() {quadrados := make(map[int]int, 15)

    for n := 1; n

  • 4.3. Maps Casa do Cdigo

    14^2 = 19615^2 = 225

    Repare no uso da funo sort.Ints() para ordenar o slice contendoas chaves do mapa. Para maiores detalhes sobre as funes de ordenaodisponveis neste pacote, acesse a documentao oficial (em ingls) em http://golang.org/pkg/sort/.

    Neste captulo, vimos em detalhes os tipos nativos de coleo de dados,abstraes extremamente importantes emuito utilizadas no desenvolvimentode praticamente qualquer programa.

    No captulo 5, veremos como criar abstraes ainda mais poderosas atra-vs de tipos customizados.

    62

  • Captulo 5

    Criando novos tipos

    Criar tipos customizados uma ferramenta de abstrao muito poderosa emlinguagens de programao. Go, comparada a linguagens puramente orien-tadas a objetos, tem um suporte limitado s caractersticas deste paradigma.Por exemplo, no possvel criar uma hierarquia de tipos baseada em he-rana. Composio de tipos, no entanto, suportada e encorajada em Go.

    Dois tipos podem tambm implementar uma ou mais interfaces em co-mum, tornando-os ainda mais flexveis. Veremos todas estas caractersticasnas prximas sees.

    5.1 Novos nomes para tipos existentesAlgumas linguagens dinmicas como Ruby possibilitam que o programa-dor altere o comportamento de qualquer classe ou objeto durante a execuode um programa. Em Go isto no possvel, mas podemos estender tipos

  • 5.1. Novos nomes para tipos existentes Casa do Cdigo

    existentes atravs da criao de novos tipos, por convenincia ou simples-mente para melhorar a legibilidade de um programa.

    Para demonstrar esta tcnica, vamos criar um novo tipo chamadoListaDeCompras baseado em um slice de strings:

    type ListaDeCompras []string

    func main() {lista := make(ListaDeCompras, 6)lista[0] = "Alface"lista[1] = "Pepino"lista[2] = "Azeite"lista[3] = "Atum"lista[4] = "Frango"lista[5] = "Chocolate"

    fmt.Println(lista)}

    O cdigo anterior imprimiria [Alface Pepino Azeite AtumFrango Chocolate].

    Inicialmente, o tipo ListaDeCompras no parece muito til. Porm,a grande vantagem em criar tipos customizados que podemos estend-los,algo impossvel de se fazer com os tipos padres da linguagem. Vamos es-tender o tipo ListaDeCompras para facilitar a nossa vida quando formosao supermercado, definindo ummtodo que separa os elementos da lista emcategorias:

    func (lista ListaDeCompras) Categorizar() ([]string, []string, []string) {

    var vegetais, carnes, outros []string

    for _, e := range lista {switch e {case "Alface", "Pepino":

    vegetais = append(vegetais, e)case "Atum", "Frango":

    64

  • Casa do Cdigo Captulo 5. Criando novos tipos

    carnes = append(carnes, e)default:

    outros = append(outros, e)}

    }

    return vegetais, carnes, outros}

    Considerando a mesma lista de compras do exemplo anterior, podera-mos categoriz-la da seguinte forma:

    vegetais, carnes, outros := lista.Categorizar()fmt.Println("Vegetais:", vegetais)fmt.Println("Carnes:", carnes)fmt.Println("Outros:", outros)

    Agora temos trs slices distintos contendo os elementos da lista de com-pras categorizados, e executando este cdigo teramos o seguinte resultado:

    Vegetais: [Alface Pepino]Carnes: [Atum Frango]Outros: [Azeite Chocolate]

    Repare na forma com que o comando switch foi utilizado na imple-mentao do mtodo Categorizar(), verificando mltiplos valores se-parados por vrgulas em algumas das clusulas case. Tambmutilizamos aclusula default para criar um slice chamado outros, contendo qualquervalor que no foi reconhecido pelas clusulas case especificadas anterior-mente.

    5.2 Converso entre tipos compatveisApesar do tipo ListaDeCompras ter sido criado com base no tipo[]string, na prtica eles so diferentes e no so automaticamente inter-cambiveis. Desta forma, no possvel utilizar um valor ListaDeComprasem que um []string esperado e vice-versa.

    65

  • 5.2. Converso entre tipos compatveis Casa do Cdigo

    Para contornar este problema, precisamos realizar uma converso de ti-pos (operao conhecida em Go como type conversion) manualmente. Po-demos converter um valor para outro tipo utilizando o formato T(x), ondeT o tipo destino e x o valor a ser convertido. Vejamos um exemplo con-vertendo valores entre ListaDeCompras e []string. Crie um arquivochamado cap5-conversao/conversao.go com o contedo:

    package main

    import "fmt"

    type ListaDeCompras []string

    func imprimirSlice(slice []string) {fmt.Println("Slice:", slice)

    }

    func imprimirLista(lista ListaDeCompras) {fmt.Println("Lista de compras:", lista)

    }

    func main() {lista := ListaDeCompras{"Alface", "Atum", "Azeite"}slice := []string{"Alface", "Atum", "Azeite"}

    imprimirSlice([]string(lista))imprimirLista(ListaDeCompras(slice))

    }

    Podemos agora executar este programa e verificar o resultado da conver-so entre os tipos:

    $ go run cap5-conversao/conversao.goSlice: [Alface Atum Azeite]Lista de compras: [Alface Atum Azeite]

    66

  • Casa do Cdigo Captulo 5. Criando novos tipos

    5.3 Criando abstraes mais poderosasJ vimos no captulo 4 que podemos facilmente manipular slices utilizandoapenas operaes de slicing e a funo append(). Podemos tambm criaruma camada de abstrao sobre os slices e implementar uma lista genrica quearmazena valores de qualquer tipo e possui operaes para remover valoresdo incio, do meio e do fim da lista. Primeiro vejamos a definio do tipopropriamente dito:

    type ListaGenerica []interface{}

    Criamos um tipo chamado ListaGenerica baseado em um slice quearmazena valores do tipo interface{}, o que permite que a lista armazenevalores de qualquer tipo.

    Agora podemos criar os mtodos, comeando pelo mtodo que removevalores de um ndice especfico:

    func (lista *ListaGenerica) RemoverIndice(indice int) interface{} {

    l := *listaremovido := l[indice]*lista = append(l[0:indice], l[indice+1:]...)return removido

    }

    Repare que definimos o tipo do receptor como *ListaGenerica umponteiro para um valor do tipo ListaGenerica pois desejamos alteraro contedo da lista. Inicialmente definimos uma varivel l para facilitar asoperaes sobre o ponteiro. Depois atribumos o valor presente no ndice es-pecificado da lista varivel removido, que ser retornada ao final do m-todo. Tendo o valor desejado em mos, alteramos a lista para desconsideraro elemento removido, e finalmente retornamos o valor removido.

    Para implementar o mtodo que remove valores do incio da lista, pode-mos simplesmente chamar omtodo RemoverIndice() passando 0 comoargumento:

    67

  • 5.3. Criando abstraes mais poderosas Casa do Cdigo

    func (lista *ListaGenerica) RemoverInicio() interface{} {return lista.RemoverIndice(0)

    }

    Isto funciona porque, substituindo o indice na operao que altera alista por 0, obtemos a expresso append(l[0:0], l[1:]...), ou seja,adicionamos os elementos da lista iniciando no ndice 1 lista vazia retor-nada pela operao l[0:0].

    De maneira anloga, podemos implementar o mtodo que remove valo-res do final da lista, novamente recorrendo ao mtodo RemoverIndice(),agora passando o ltimo ndice da lista como argumento:

    func (lista *ListaGenerica) RemoverFim() interfa