Usando Sistemas de Tipo em Funções no Go

Usando sistema de tipos em funções no Go

Olá leitor, seja bem vindo a mais uma lição da jornada GoLang 🥳

Durante sua jornada como desenvolvedor de sistemas em Go, você vai se deparar com funções que implementam e recebem diversos tipos básicos, compostos e até tipos mais avançados.

O objetivo desta lição, é te ensinar a como mesclar todos os tipos que você já aprendeu até aqui, com o uso de funções e métodos em Go.

Vamos nessa?

Criando seu projeto de testes

Dentro da pasta JornadaGoLang, nós iremos criar uma nova pasta chamada de 12-sistemas-de-tipos-com-funcoes, onde dentro dela, vamos criar o nosso arquivo main.go:

package main

func main(){

}

Agora, vamos começar pelos tipos básicos 😉

Funções com Tipos Básicos

Na lição que falava sobre tipos básicos, você aprendeu que a linguagem GoLang possuí diversos tipos como: int, float64, string, bool, entre outros

E como você já deve saber, é possível criar uma função que não só seja capaz de receber tais tipos (dentro de variáveis temporárias), como também retorná-los:

package main

import "fmt"

func soma(a int, b int) int {
    return a + b
}

func main() {
    resultado := soma(10, 5)
    fmt.Println("Soma:", resultado)
}

No código acima, criamos uma variável chamada resultado, que recebe dois tipos inteiros e retorna um inteiro.

Funções com Tipos Compostos (Array e Slice)

Na lição que falava sobre tipos compostos, você aprendeu que a linguagem possuí apenas três deles: array, slice e map.

Tais tipos podem ser recebidos por uma função, observe:

package main

import "fmt"

func somaElementos(numeros []int) int {
    total := 0
    for _, num := range numeros {
        total += num
    }
    return total
}

func main() {
    numeros := []int{1, 2, 3, 4, 5}
    fmt.Println("Soma dos elementos:", somaElementos(numeros))
}

No código acima, criamos uma função chamada somaElementos que recebe um slice de números inteiros e retorna um valor inteiro.

Além disso, você pode retornar um array ou um slice caso desejar:

package main

import "fmt"

// RetornaSlice recebe dois números e os retorna em um slice.
func RetornaSlice(a, b int) []int {
    return []int{a, b}
}

func main() {
    numeros := RetornaSlice(10, 20)
    fmt.Println("Slice retornado:", numeros)
}

No caso de um map,  podemos utilizá-los como parâmetros de retornos de funções da seguinte forma:

package main

import "fmt"

func contarPalavras(texto string) map[rune]int {
    contagem := make(map[rune]int)
    for _, letra := range texto {
        contagem[letra]++
    }
    return contagem
}

func main() {
    resultado := contarPalavras("banana")
    fmt.Println("Frequência das letras:", resultado)
}

No código acima, estamos recebendo uma string, e retornando um map. Nós também podemos receber um map da mesma forma como fizemos acima no exemplo do uso de uma slice e um array:

package main

import "fmt"

// MapParaString recebe um map de string para string e retorna uma representação em formato de texto.
func MapParaString(m map[string]string) string {
    resultado := ""
    for chave, valor := range m {
        resultado += fmt.Sprintf("%s: %s\n", chave, valor)
    }
    return resultado
}

func main() {
    dados := map[string]string{
        "Nome":    "Carlos",
        "Idade":   "30",
        "Cidade":  "São Paulo",
    }

    resultado := MapParaString(dados)
    fmt.Println("String gerada:\n", resultado)
}

Funções com Structs

Na lição que falava sobre tipos avançados em Go, você aprendeu a utilizar uma struct, certo?

Sendo assim, saiba que nós podemos definir funções que trabalham com structs, de forma a encapsular dados e comportamentos, observe:

package main

import "fmt"

type Pessoa struct {
    Nome string
    Idade int
}

func (p Pessoa) Saudacao() string {
    return fmt.Sprintf("Olá, meu nome é %s e tenho %d anos.", p.Nome, p.Idade)
}

func main() {
    pessoa := Pessoa{"Carlos", 30}
    fmt.Println(pessoa.Saudacao())
}

Além disso, podemos receber um conjunto de tipos básicos e retornar em um formato de struct:

package main

import "fmt"

// Pessoa representa uma estrutura com nome, idade e cidade.
type Pessoa struct {
    Nome   string
    Idade  int
    Cidade string
}

// NovaPessoa recebe valores básicos e retorna uma struct Pessoa.
func NovaPessoa(nome string, idade int, cidade string) Pessoa {
    return Pessoa{
        Nome:   nome,
        Idade:  idade,
        Cidade: cidade,
    }
}

func main() {
    pessoa := NovaPessoa("Ana", 28, "Rio de Janeiro")
    fmt.Printf("Pessoa: %+v\n", pessoa)
}

Métodos com Ponteiros

Apesar de não termos entrado neste assunto ainda, é possível criar métodos que fazem uso de ponteiros em Go:

package main

import "fmt"

type Contador struct {
    Valor int
}

func (c *Contador) Incrementar() {
    c.Valor++
}

func main() {
    c := Contador{Valor: 0}
    c.Incrementar()
    fmt.Println("Valor do contador:", c.Valor)
}

Funções com Interfaces

Ainda na lição de tipos avançados em Go, também é possível implementar interfaces por meio de funções genéricas:

package main

import "fmt"

type Forma interface {
    Area() float64
}

type Quadrado struct {
    Lado float64
}

func (q Quadrado) Area() float64 {
    return q.Lado * q.Lado
}

type Circulo struct {
    Raio float64
}

func (c Circulo) Area() float64 {
    return 3.14 * c.Raio * c.Raio
}

func imprimirArea(f Forma) {
    fmt.Println("Área da forma:", f.Area())
}

func main() {
    q := Quadrado{Lado: 4}
    c := Circulo{Raio: 3}

    imprimirArea(q)
    imprimirArea(c)
}

É possível criar uma função em GoLang que retorna uma interface?

Sim! Em Go, é totalmente possível criar uma função que retorne uma interface. Isso é útil quando você deseja que a função retorne diferentes tipos que implementam um comportamento comum.

Vejamos um exemplo:

package main

import "fmt"

// Interface Forma define um comportamento comum.
type Forma interface {
    Area() float64
}

// Estrutura Quadrado
type Quadrado struct {
    Lado float64
}

// Método Area() para Quadrado (implementa Forma)
func (q Quadrado) Area() float64 {
    return q.Lado * q.Lado
}

// Estrutura Circulo
type Circulo struct {
    Raio float64
}

// Método Area() para Circulo (implementa Forma)
func (c Circulo) Area() float64 {
    return 3.14 * c.Raio * c.Raio
}

// NovaForma retorna uma interface Forma com base no tipo.
func NovaForma(tipo string, valor float64) Forma {
    if tipo == "quadrado" {
        return Quadrado{Lado: valor}
    }
    return Circulo{Raio: valor}
}

func main() {
    forma := NovaForma("quadrado", 5)
    fmt.Println("Área da forma:", forma.Area())

    outraForma := NovaForma("circulo", 3)
    fmt.Println("Área da outra forma:", outraForma.Area())
}

Incrível, não acha? 😉

Uso de interface{} e Generics

Como você viu na lição passada, a partir do Go 1.18+, podemos usar generics para criar funções que operam em múltiplos tipos, e isso também pode se aplicar a funções:

package main

import "fmt"

func Soma[T int | float64](a, b T) T {
    return a + b
}

func main() {
    fmt.Println(Soma(10, 20))       // Inteiros
    fmt.Println(Soma(5.5, 2.3))     // Floats
}

Repositório da lição

Todos os arquivos relacionados com esta lição, podem ser encontrados nos seguintes repositórios abaixo:

Conclusão

Sim, está lição foi bem básica, e talvez nem tenha dado tempo de se aquecer 😩

A ideia desta lição foi justamente sintetizar e mesclar os conteúdos que aprendemos nas lições de:

Até a próxima 🤖

Criadores de Conteúdo

Foto do William Lima
William Lima
Fundador da Micilini

Inventor nato, escreve conteudos de programação para o portal da micilini.

Torne-se um MIC 🤖

Mais de 100 mic's já estão conectados na plataforma.