Múltiplos channels e a cláusula select

Dando continuidade ao nosso estudo de goroutines e channels, nesse post vamos falar sobre uma cláusula pouco utilizada.

Antes de começar, vou deixar os links para os outros posts caso você tenha perdido algum da série.

A cláusula select é utilizada para que uma função consiga trabalhar com múltiplos channels. Ela bloqueia a execução da função até que um dos channels esteja pronto para ser executado. Caso mais de um channel esteja pronto para ser executado, ela selecionará de forma aleatória qual executar.

Para tentar ficar um pouco mais claro, vamos escrever um pequeno programa para ilustrar o comportamento.

Leia mais »

Formatando strings para logs e mensagens

Se você já trabalha com Go a algum tempo, muito provavelmente você já conhece e utiliza o %s, %d e o \n. Pois bem, nesse post vamos abordar os principais os verbos disponíveis para te ajudar na hora de formatar uma string em Go.

Se você é novo em Go, saiba que os verbos que vamos abordar nesse post podem ser utilizados com as funções Printf, Sprintf e Errorf do package fmt, assim como as funções Fatalf, Panicf e Printf do package log.

Para começar, vamos a um exemplo bem simples utilizando somente os dois verbos que já comentamos. Digamos que nosso programa, sempre que alguém pede um novo café, exibe o nome da pessoa e a quantidade de café que ela consumiu no dia.

func main() {
    name := "Tiago"
    coffee := 5

    fmt.Printf("Olá %s, você já bebeu %d cafés hoje", name, coffee)
}

Ao ser executado, o %s será substituido pelo conteúdo da váriavel name, e o %d pela vafiável coffee.

Leia mais »

Trabalhando com switch/case

Dando continuidade nos nossos posts sobre comandos core da linguagem, nesse post vamos falar sobre o switch/case.

Esse comando é bem conhecido e existe em quase toda linguagem de programação. Se você nunca ouviu falar dele, tente imaginá-lo como uma forma mais curta de encadear um monte de if - else.

Uma diferença que vale ressaltar entre Go e as outras linguagens é que ao contrário das outras linguagens, o Go só executa o caso selecionado eliminando assim a necessidade de colocar um break.

Em sua forma mais básica, ele recebe um valor no inicio e depois checa se esse valor é igual algum dos casos definidos, e caso não seja, executa a caso padrão.

Para iniciar, vamos a um exemplo básico.

switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("MAC OS")
case "linux":
    fmt.Println("Linux")
default:
    fmt.Printf("%s \n", os)
}
Leia mais »

Convertendo tipos de dados

Em linguagens fortemente tipadas, muitas vezes precisamos fazer conversões dos tipos das nossas variáveis para poder executar algum tipo de função interna. Uma das conversões mais comum que acontecem é a de int ou float para string e vice versa.

Também pode acontecer conversões dentro do mesmo “grupo”, por exemplo de int32 para int16. Porém, embora pertençam ao mesmo “grupo”, temos que ficar atentos ao tamanho máximo que cada um desses tipos comportam, pois caso o valor extrapole a capacidade do tipo destino, a conversão irá dar errado.

var i32 int32 = 10
i16 := int16(i32)
fmt.Printf("%T - %d", i16, i16)

No exemplo acima, tudo funcionará bem, pois 10 é um valor que os dois tipos de dados comportam. Agora se o valor de i32 fosse 32769, a conversão para int16 retornaria -32767, e se fosse algo maior ainda, como por exemplo 132769, a conversão retornaria 1697.

Leia mais »

O que são e como funcionam as Goroutines

Na semana passada explicamos a diferença entre concorrência e paralelismo (link do post), o que serviu de base para o post de hoje, onde vamos explicar as famosas goroutines.

Goroutines são funções ou métodos executados em concorrência. Podemos pensar nelas como uma especie de lightweight thread que são gerenciadas pelo runtime do Go.

Chamamos de lightweight thread pois o custo para sua criação é muito menor quando comparada com um thread de verdade. Outro ponto positivo é que o runtime consegue aumentar ou diminuir a quantidade de goroutines de acordo com a necessidade da aplicação, enquanto o número de thread normalmente é fixo.

Leia mais »

Entendendo os tipos de dados e suas capacidades

Embora seja um assunto não muito abordado, pelo menos no meu ponto de vista, entender todos os tipos de dados que uma linguagem oferece, ajuda muito no momento de criar um programa, principalmente do ponto de vista de performance e eficiência no consumo de recursos.

Por isso, no post de hoje vamos ver todos os tipos de dados básicos que temos na linguagem e suas capacidades.

Bool

O tipo booleano, é um dos tipos mais básicos e comum no mundo da tecnologia.

Embora em algumas linguagens ele possa ser definido com 0 e 1, em Go os únicos valores aceitos são true ou false.

String

Conjunto de todos os caracteres de 8-bit, ou seja, qualquer tipo de texto.

Uma string pode ser vazia, mas não pode ser nula (nil).

Leia mais »

Trabalhando com variáveis

Embora pareça um assunto sem muito conteúdo, é muito importante saber alguns detalhes de como trabalhar com variáveis em Go.

A primeira coisa que precisamos saber sobre variáveis em Go, é que elas tem escopo, ou seja, dependendo de onde ela for criada você estará limitando os locais em que ela estará disponível.

No exemplo abaixo, vamos criar 3 variáveis com 3 escopos diferentes.

var connection = database.Connect()

func Insert(data interface{}) string {
    err := connection.Create(data)
    if err != nil {
        msg := fmt.Sprintf("falha ao tentar inserir: %v", err)
        return msg
    }

    return "inserido com sucesso sucesso!"
}

connection: Essa variável tem um escopo global, ou seja, estará disponível para ser utilizada em qualquer parte do package onde ela foi criada.

Leia mais »

Escrevendo testes unitários

Testes unitários nos ajudam muito a tentar manter a quantidade de bugs baixa, já que podemos testar várias hipóteses de forma automática. E por ser automatizada, a cada mudança que fazemos, os testes unitários também nos ajudam a garantir que não quebramos o sistema em partes que nem tocamos mas que dependem das mudanças que fizemos.

Embora exista alguns packages que ajudam na escrita dos testes unitários, a recomendação do time de desenvolvimento da linguagem é que os testes sejam escritos usando o package nativo da linguagem, pelo simples fato de que um package externo pode adicionar algum bug ou realizar algum teste de forma incorreta.

Antes de começar a escrever os testes, vamos criar um arquivo chamado soma.go e escrever uma pequena função que soma N valores.

package soma


func Soma(valores ...int) (total int) {
    for _, valor := range valores {
        total += valor
    }

    return
}
Leia mais »

Escrevendo funções em GO

Hoje vamos falar um pouco sobre um dos itens mais básico e importante de qualquer linguagem de programação, funções.

Funções nos ajudam a dividir o código de forma a ser reutilizável, diminuindo a quantidade de escrita de um bloco de código e facilitando a manutenção do programa. É claro que se alguém fizer uma função com 17 mil linhas de código, toda bagunçada e confusa, nada do que eu disse antes se torna verdade, mas sabemos que ninguém aqui faz ou já fez isso, certo? 😀

Assim como em qualquer outra linguagem, a premissa de uma função em Go é que ela execute somente aquilo que ela nasceu para executar. Dessa forma diminuímos a complexidade da função e aumentamos a possibilidade de reutilização da mesma.

Leia mais »

As várias formas de fazer looping em Go

A maioria das linguagens que já trabalhei tem vários comandos para fazer looping, como por exemplo for, foreach, while, do..while e goto.

Já em Go, nós só temos o comando for, porém ele tem várias assinaturas, o que no final faz com que ele possa ter o comportamento equivalente a um for, foreach e while de outras linguagens.

Vamos começar pela forma mais básica, que é a mesma assinatura que outras linguagens utilizam.

for i := 0; i < 10; i++ {
    fmt.Println(i)
}

nomes := []string{Tiago, Lucas, Pedro, Luana, Dani}
for i := 0; i < len(nomes); i++ {
    fmt.Println(nomes[i])
}
Leia mais »