Tutorial de Golang
ยฟQuรฉ es ir?
Go (tambiรฉn conocido como Golang) es un lenguaje de programaciรณn de cรณdigo abierto desarrollado por Google. Es un lenguaje compilado de tipado estรกtico. Go admite programaciรณn concurrente, es decir, permite ejecutar mรบltiples procesos simultรกneamente. Esto se logra mediante canales, goroutines, etc. El lenguaje Go tiene recolecciรณn de basura que se encarga de la gestiรณn de memoria y permite la ejecuciรณn diferida de funciones.
Aprenderemos todos los conceptos bรกsicos de Golang en este tutorial de Learn Go Language.
Cรณmo descargar e instalar GO
Paso 1) Vaya a https://golang.org/dl/. Descarga el binario para tu sistema operativo.
Paso 2) Double Haga clic en el instalador y haga clic en Ejecutar.
Paso 3) Haga clic en Siguiente

Paso 4) Seleccione la carpeta de instalaciรณn y haga clic en Siguiente.

Paso 5) Haga clic en Finalizar una vez que se complete la instalaciรณn.

Paso 6) Una vez completada la instalaciรณn podrรกs verificarla abriendo el terminal y escribiendo
go version
Esto mostrarรก la versiรณn de go instalada.

Su programa First Go: ยกGo Hello World!
Crea una carpeta llamada StudyGo. En este tutorial de lenguaje Go, crearemos nuestros programas Go dentro de esta carpeta. Los archivos Go se crean con la extensiรณn .ir. Puede ejecutar programas Go usando la sintaxis
go run <filename>
Cree un archivo llamado first.go, agregue el siguiente cรณdigo y guรกrdelo.
package main
import ("fmt")
func main() {
fmt.Println("Hello World! This is my first Go program\n")
}

Navegue a esta carpeta en su terminal. Ejecute el programa usando el comando
Ve a correr primero.
Puedes ver la impresiรณn de salida.
Hello World! This is my first Go program

Ahora analicemos el programa anterior.
paquete principal: cada programa de Go Language debe comenzar con un nombre de paquete. Go nos permite usar paquetes en otros programas de Go y, por lo tanto, admite la reutilizaciรณn del cรณdigo. La ejecuciรณn de un programa Go comienza con el cรณdigo dentro del paquete llamado main.
import fmt: importa el paquete fmt. Este paquete implementa las funciones de E/S.
func main(): esta es la funciรณn desde la cual comienza la ejecuciรณn del programa. La funciรณn principal siempre debe colocarse en el paquete principal. En main(), puedes escribir el cรณdigo dentro de {}.
fmt.Println: esto imprimirรก el texto en la pantalla mediante la funciรณn Println de fmt.
Nota: En las secciones siguientes de este tutorial de Go, cuando mencionas ejecutar/ejecutar el cรณdigo, significa guardar el cรณdigo en un archivo con extensiรณn .go y ejecutarlo usando la sintaxis.
go run <filename>
Tipos de datos
Los tipos (tipos de datos) representan el tipo de valor almacenado en una variable, el tipo de valor que devuelve una funciรณn, etc.
Hay tres tipos bรกsicos en Go Language
Tipos numรฉricos โ Representan valores numรฉricos que incluyen valores enteros, de punto flotante y complejos. Los distintos tipos numรฉricos son:
int8: enteros de 8 bits con signo.
int16: enteros de 16 bits con signo.
int32: enteros de 32 bits con signo.
int64: enteros de 64 bits con signo.
uint8: enteros sin signo de 8 bits.
uint16: enteros sin signo de 16 bits.
uint32: enteros sin signo de 32 bits.
uint64: enteros sin signo de 64 bits.
float32: nรบmeros de coma flotante de 32 bits.
float64: nรบmeros de coma flotante de 64 bits.
complex64 โ tiene partes reales e imaginarias float32.
complex128 โ tiene partes reales e imaginarias float32.
Tipos de cuerdas โ Representa una secuencia de bytes (caracteres). Se pueden realizar varias operaciones con cadenas, como concatenaciรณn de cadenas, extracciรณn de subcadenas, etc.
Tipos booleanos โ Representa 2 valores, verdadero o falso.
Interfaz de Golang
Interfaz de Golang es una colecciรณn de firmas de mรฉtodos utilizadas por un tipo para implementar el comportamiento de los objetos. El objetivo principal de la interfaz Golang es proporcionar firmas de mรฉtodos con nombres, argumentos y tipos de retorno. Depende de un tipo declarar e implementar el mรฉtodo. Se puede declarar una interfaz en Golang utilizando la palabra clave "interfaz".
Variables
Las variables apuntan a una ubicaciรณn de memoria que almacena algรบn tipo de valor. El parรกmetro de tipo (en la sintaxis siguiente) representa el tipo de valor que se puede almacenar en la ubicaciรณn de la memoria.
La variable se puede declarar usando la sintaxis
var <variable_name> <type>
Una vez que declara una variable de un tipo, puede asignar la variable a cualquier valor de ese tipo.
Tambiรฉn puede dar un valor inicial a una variable durante la declaraciรณn misma usando
var <variable_name> <type> = <value>
Si declara la variable con un valor inicial, vaya e infiera el tipo de variable a partir del tipo de valor asignado. Entonces puedes omitir el tipo durante la declaraciรณn usando la sintaxis
var <variable_name> = <value>
Ademรกs, puedes declarar mรบltiples variables con la sintaxis
var <variable_name1>, <variable_name2> = <value1>, <value2>
El siguiente programa en este tutorial de Go tiene algunos ejemplos de declaraciones de variables en Golang.
package main
import "fmt"
func main() {
//declaring a integer variable x
var x int
x=3 //assigning x the value 3
fmt.Println("x:", x) //prints 3
//declaring a integer variable y with value 20 in a single statement and prints it
var y int=20
fmt.Println("y:", y)
//declaring a variable z with value 50 and prints it
//Here type int is not explicitly mentioned
var z=50
fmt.Println("z:", z)
//Multiple variables are assigned in single line- i with an integer and j with a string
var i, j = 100,"hello"
fmt.Println("i and j:", i,j)
}
La salida serรก
x: 3 y: 20 z: 50 i and j: 100 hello
Go Language tambiรฉn proporciona una manera fรกcil de declarar las variables con valor omitiendo la palabra clave var usando
<variable_name> := <value>
Tenga en cuenta que utilizรณ := en lugar de =. No puedes usar := solo para asignar un valor a una variable que ya estรก declarada. := se utiliza para declarar y asignar valor.
Crea un archivo llamado allow.go con el siguiente cรณdigo
package main
import ("fmt")
func main() {
a := 20
fmt.Println(a)
//gives error since a is already declared
a := 30
fmt.Println(a)
}
Ejecute go run asignar.go para ver el resultado como
./assign.go:7:4: no new variables on left side of :=
Las variables declaradas sin un valor inicial tendrรกn 0 para tipos numรฉricos, falso para booleanos y cadena vacรญa para cadenas.
Constantes
Las variables constantes son aquellas variables cuyo valor no se puede cambiar una vez asignado. Una constante en el lenguaje de programaciรณn Go se declara utilizando la palabra clave "const"
Crea un archivo llamado constant.go y con el siguiente cรณdigo
package main
import ("fmt")
func main() {
const b =10
fmt.Println(b)
b = 30
fmt.Println(b)
}
Ejecute go run constante.go para ver el resultado como
.constant.go:7:4: cannot assign to b
Para ejemplos de bucles
Los bucles se utilizan para ejecutar un bloque de declaraciones repetidamente segรบn una condiciรณn. La mayorรญa de los lenguajes de programaciรณn proporcionan 3 tipos de bucles: for, while y do while. Pero el lenguaje de programaciรณn Go solo admite bucles.
La sintaxis de un bucle for de Golang es
for initialisation_expression; evaluation_expression; iteration_expression{
// one or more statement
}
La expresiรณn_inicializaciรณn se ejecuta primero (y solo una vez) en el bucle for de Golang.
Luego se evalรบa la expresiรณn_evaluaciรณn y, si es verdadera, se ejecuta el cรณdigo dentro del bloque.
Se ejecuta el ID de iteration_expression y se vuelve a evaluar la expresiรณn_evaluaciรณn. Si es cierto, el bloque de instrucciones se ejecuta nuevamente. Esto continuarรก hasta que la expresiรณn_evaluaciรณn se vuelva falsa.
Copie el siguiente programa en un archivo y ejecรบtelo para ver Golang for loop imprimiendo nรบmeros del 1 al 5
package main
import "fmt"
func main() {
var i int
for i = 1; i <= 5; i++ {
fmt.Println(i)
}
}
La salida es
1 2 3 4 5
Si mas
Si no, es una declaraciรณn condicional. La sinaxis es
if condition{
// statements_1
}else{
// statements_2
}
Aquรญ se evalรบa la condiciรณn y, si es verdadera, se ejecutarรกn las declaraciones_1; de lo contrario, se ejecutarรก la declaraciรณn_2.
Tambiรฉn puedes usar la declaraciรณn if sin mรกs. Tambiรฉn puede tener declaraciones if else encadenadas. Los siguientes programas explicarรกn mรกs sobre if else.
Ejecute el siguiente programa. Comprueba si un nรบmero, x, es menor que 10. Si es asรญ, imprimirรก "x es menor que 10".
package main
import "fmt"
func main() {
var x = 50
if x < 10 {
//Executes if x < 10
fmt.Println("x is less than 10")
}
}
Aquรญ, dado que el valor de x es mayor que 10, la declaraciรณn dentro de la condiciรณn del bloque if no se ejecutarรก.
Ahora vea el siguiente programa. En este tutorial del lenguaje de programaciรณn Go, tenemos un bloque else que se ejecutarรก si falla la evaluaciรณn if.
package main
import "fmt"
func main() {
var x = 50
if x < 10 {
//Executes if x is less than 10
fmt.Println("x is less than 10")
} else {
//Executes if x >= 10
fmt.Println("x is greater than or equals 10")
}
}
Este programa le darรก resultados.
x is greater than or equals 10
Ahora, en este tutorial de Go, veremos un programa con mรบltiples bloques if else (encadenados if else). Ejecute el siguiente ejemplo de Go. Comprueba si un nรบmero es menor que 10, entre 10 y 90 o mayor que 90.
package main
import "fmt"
func main() {
var x = 100
if x < 10 {
//Executes if x is less than 10
fmt.Println("x is less than 10")
} else if x >= 10 && x <= 90 {
//Executes if x >= 10 and x<=90
fmt.Println("x is between 10 and 90")
} else {
//Executes if both above cases fail i.e x>90
fmt.Println("x is greater than 90")
}
}
Aquรญ primero la condiciรณn if verifica si x es menor que 10 y no lo es. Entonces verifica la siguiente condiciรณn (si no) si estรก entre 10 y 90, lo cual tambiรฉn es falso. Luego ejecuta el bloque en la secciรณn else que proporciona la salida
x is greater than 90
Interruptor
Switch es otra declaraciรณn condicional. Las declaraciones de cambio evalรบan una expresiรณn y el resultado se compara con un conjunto de valores disponibles (casos). Una vez que se encuentra una coincidencia, se ejecutan las declaraciones asociadas con esa coincidencia (caso). Si no se encuentra ninguna coincidencia, no se ejecutarรก nada. Tambiรฉn puede agregar un caso predeterminado para cambiar que se ejecutarรก si no se encuentran otras coincidencias. La sintaxis del interruptor es
switch expression {
case value_1:
statements_1
case value_2:
statements_2
case value_n:
statements_n
default:
statements_default
}
Aquรญ el valor de la expresiรณn se compara con los valores de cada caso. Una vez que se encuentra una coincidencia, se ejecutan las declaraciones asociadas con ese caso. Si no se encuentra ninguna coincidencia, se ejecutan las declaraciones de la secciรณn predeterminada.
Ejecute el siguiente programa
package main
import "fmt"
func main() {
a,b := 2,1
switch a+b {
case 1:
fmt.Println("Sum is 1")
case 2:
fmt.Println("Sum is 2")
case 3:
fmt.Println("Sum is 3")
default:
fmt.Println("Printing default")
}
}
Obtendrรก la salida como
Sum is 3
Cambie el valor de a y b a 3 y el resultado serรก
Printing default
Tambiรฉn puede tener varios valores en un caso separรกndolos con una coma.
Matrices
La matriz representa una secuencia nombrada de tamaรฑo fijo de elementos del mismo tipo. No puede tener una matriz que contenga nรบmeros enteros y caracteres. No puede cambiar el tamaรฑo de una matriz una vez que define el tamaรฑo.
La sintaxis para declarar una matriz es
var arrayname [size] type
A cada elemento de la matriz se le puede asignar un valor usando la sintaxis
arrayname [index] = value
El รญndice de matriz comienza desde 0 a tamaรฑo-1.
Puede asignar valores a elementos de matriz durante la declaraciรณn usando la sintaxis
arrayname := [size] type {value_0,value_1,โฆ,value_size-1}
Tambiรฉn puede ignorar el parรกmetro de tamaรฑo al declarar la matriz con valores reemplazando tamaรฑo con ... y el compilador encontrarรก la longitud a partir del nรบmero de valores. La sintaxis es
arrayname := [โฆ] type {value_0,value_1,โฆ,value_size-1}
Puede encontrar la longitud de la matriz usando la sintaxis
len(arrayname)
Ejecute el siguiente ejemplo de Go para comprender la matriz
package main
import "fmt"
func main() {
var numbers [3] string //Declaring a string array of size 3 and adding elements
numbers[0] = "One"
numbers[1] = "Two"
numbers[2] = "Three"
fmt.Println(numbers[1]) //prints Two
fmt.Println(len(numbers)) //prints 3
fmt.Println(numbers) // prints [One Two Three]
directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements
fmt.Println(directions) //prints [1 2 3 4 5]
fmt.Println(len(directions)) //prints 5
//Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array)
//fmt.Println(directions[5])
}
Salida
Two 3 [One Two Three] [1 2 3 4 5] 5
Funciรณn Golang Slice y Append
Un sector es una porciรณn o segmento de una matriz. O es una vista o vista parcial de una matriz subyacente a la que apunta. Puede acceder a los elementos de un sector utilizando el nombre del sector y el nรบmero de รญndice tal como lo hace en una matriz. No puede cambiar la longitud de una matriz, pero puede cambiar el tamaรฑo de un segmento.
Los contenidos de un segmento son en realidad punteros a los elementos de una matriz. Significa Si cambia cualquier elemento en un segmento, el contenido de la matriz subyacente tambiรฉn se verรก afectado.
La sintaxis para crear un sector es
var slice_name [] type = array_name[start:end]
Esto crearรก un segmento llamado nombre_sector a partir de una matriz llamada nombre_matriz con los elementos en el รญndice de principio a fin-1.
Ahora, en este tutorial de Golang, ejecutaremos el siguiente programa. El programa crearรก una porciรณn de la matriz y la imprimirรก. Ademรกs, puede ver que modificar el contenido del segmento modificarรก la matriz real.
package main
import "fmt"
func main() {
// declaring array
a := [5] string {"one", "two", "three", "four", "five"}
fmt.Println("Array after creation:",a)
var b [] string = a[1:4] //created a slice named b
fmt.Println("Slice after creation:",b)
b[0]="changed" // changed the slice data
fmt.Println("Slice after modifying:",b)
fmt.Println("Array after slice modification:",a)
}
Esto imprimirรก el resultado como
Array after creation: [one two three four five] Slice after creation: [two three four] Slice after modifying: [changed three four] Array after slice modification: [one changed three four five]
Hay ciertas funciones como Golang len, Golang append que puedes aplicar en sectores
len(nombre_porciรณn) โ devuelve la longitud del segmento
agregar(nombre_porciรณn, valor_1, valor_2) โ Golang append se utiliza para agregar valor_1 y valor_2 a un segmento existente.
agregar(slice_nale1,slice_name2โฆ) โ aรฑade nombre_porciรณn2 a nombre_porciรณn1
Ejecute el siguiente programa.
package main
import "fmt"
func main() {
a := [5] string {"1","2","3","4","5"}
slice_a := a[1:3]
b := [5] string {"one","two","three","four","five"}
slice_b := b[1:3]
fmt.Println("Slice_a:", slice_a)
fmt.Println("Slice_b:", slice_b)
fmt.Println("Length of slice_a:", len(slice_a))
fmt.Println("Length of slice_b:", len(slice_b))
slice_a = append(slice_a,slice_b...) // appending slice
fmt.Println("New Slice_a after appending slice_b :", slice_a)
slice_a = append(slice_a,"text1") // appending value
fmt.Println("New Slice_a after appending text1 :", slice_a)
}
La salida serรก
Slice_a: [2 3] Slice_b: [two three] Length of slice_a: 2 Length of slice_b: 2 New Slice_a after appending slice_b : [2 3 two three] New Slice_a after appending text1 : [2 3 two three text1]
El programa primero crea 2 cortes e imprime su longitud. Luego agregรณ un segmento a otro y luego agregรณ una cadena al segmento resultante.
Funciones
Una funciรณn representa un bloque de declaraciones que realiza una tarea especรญfica. Una declaraciรณn de funciรณn nos dice el nombre de la funciรณn, el tipo de retorno y los parรกmetros de entrada. La definiciรณn de funciรณn representa el cรณdigo contenido en la funciรณn. La sintaxis para declarar la funciรณn es
func function_name(parameter_1 type, parameter_n type) return_type {
//statements
}
Los parรกmetros y tipos de retorno son opcionales. Ademรกs, puede devolver varios valores de una funciรณn.
Ahora, en este tutorial de Golang, ejecutemos el siguiente ejemplo de Golang. Aquรญ, la funciรณn denominada calc aceptarรก dos nรบmeros y realizarรก la suma y la resta y devolverรก ambos valores.
package main
import "fmt"
//calc is the function name which accepts two integers num1 and num2
//(int, int) says that the function returns two values, both of integer type.
func calc(num1 int, num2 int)(int, int) {
sum := num1 + num2
diff := num1 - num2
return sum, diff
}
func main() {
x,y := 15,10
//calls the function calc with x and y an d gets sum, diff as output
sum, diff := calc(x,y)
fmt.Println("Sum",sum)
fmt.Println("Diff",diff)
}
La salida serรก
Sum 25 Diff 5
Paquetes
Los paquetes se utilizan para organizar el cรณdigo. En un proyecto grande, no es factible escribir cรณdigo en un solo archivo. El lenguaje de programaciรณn Go nos permite organizar el cรณdigo en diferentes paquetes. Esto aumenta la legibilidad y reutilizaciรณn del cรณdigo. Un programa Go ejecutable debe contener un paquete llamado main y la ejecuciรณn del programa comienza desde la funciรณn llamada main. Puede importar otros paquetes en nuestro programa usando la sintaxis
import package_name
Veremos y discutiremos en este tutorial de Golang, cรณmo crear y usar paquetes en el siguiente ejemplo de Golang.
Paso 1) Cree un archivo llamado package_example.go y agregue el siguiente cรณdigo
package main
import "fmt"
//the package to be created
import "calculation"
func main() {
x,y := 15,10
//the package will have function Do_add()
sum := calculation.Do_add(x,y)
fmt.Println("Sum",sum)
}
En el programa anterior, fmt es un paquete que el lenguaje de programaciรณn Go nos proporciona principalmente para fines de E/S. Ademรกs, puede ver un paquete llamado cรกlculo. Dentro de main() puedes ver una suma de pasos: = cรกlculo.Do_add(x,y). Significa que estรกs invocando la funciรณn Do_add desde el cรกlculo del paquete.
Paso 2) Primero, debe crear el cรกlculo del paquete dentro de una carpeta con el mismo nombre en la carpeta src de go. La ruta instalada de go se puede encontrar en la variable PATH.
Para mac, busque la ruta ejecutando echo $PATH

Entonces la ruta es /usr/local/go
Para Windows, busque la ruta ejecutando echo %GOROOT%

Aquรญ la ruta es C:\Go\
Paso 3) Vaya a la carpeta src (/usr/local/go/src para Mac y C:\Go\src para Windows). Ahora, segรบn el cรณdigo, el nombre del paquete es cรกlculo. Go requiere que el paquete se coloque en un directorio con el mismo nombre dentro del directorio src. Cree un directorio llamado cรกlculo en la carpeta src.
Paso 4) Cree un archivo llamado calc.go (puede darle cualquier nombre, pero el nombre del paquete en el cรณdigo importa. Aquรญ deberรญa estar el cรกlculo) dentro del directorio de cรกlculo y agregue el siguiente cรณdigo
package calculation
func Do_add(num1 int, num2 int)(int) {
sum := num1 + num2
return sum
}
Paso 5) Ejecute el comando go install desde el directorio de cรกlculo que compilarรก calc.go.

Paso 6) Ahora regrese a package_example.go y ejecute go run package_example.go. La salida serรก Sum 25.
Tenga en cuenta que el nombre de la funciรณn Do_add comienza con una letra mayรบscula. Esto se debe a que en Go, si el nombre de la funciรณn comienza con una letra mayรบscula, significa que otros programas pueden verla (acceder), de lo contrario, otros programas no pueden acceder a ella. Si el nombre de la funciรณn fuera do_add, entonces habrรญa recibido el error
no se puede hacer referencia al nombre no exportado cรกlculo.calc.
Aplazar y apilar aplazamientos
Las declaraciones diferidas se utilizan para diferir la ejecuciรณn de una llamada a funciรณn hasta que la funciรณn que contiene la declaraciรณn diferida complete su ejecuciรณn.
Aprendamos esto con un ejemplo:
package main
import "fmt"
func sample() {
fmt.Println("Inside the sample()")
}
func main() {
//sample() will be invoked only after executing the statements of main()
defer sample()
fmt.Println("Inside the main()")
}
La salida serรก
Inside the main() Inside the sample()
Aquรญ la ejecuciรณn de sample() se difiere hasta que se completa la ejecuciรณn de la funciรณn adjunta (main()).
Apilar aplazar consiste en utilizar mรบltiples declaraciones de aplazar. Suponga que tiene varias declaraciones diferidas dentro de una funciรณn. Go coloca todas las llamadas a funciones diferidas en una pila y, una vez que regresa la funciรณn adjunta, las funciones apiladas se ejecutan en el Orden รบltimo en entrar, primero en salir (LIFO). Puedes ver esto en el siguiente ejemplo.
Ejecute el siguiente cรณdigo
package main
import "fmt"
func display(a int) {
fmt.Println(a)
}
func main() {
defer display(1)
defer display(2)
defer display(3)
fmt.Println(4)
}
La salida serรก
4 3 2 1
Aquรญ el cรณdigo dentro de main() se ejecuta primero, y luego las llamadas a funciones diferidas se ejecutan en orden inverso, es decir, 4, 3,2,1.
Punteros
Antes de explicar los punteros, analicemos primero el operador "&". El operador "&" se utiliza para obtener la direcciรณn de una variable. Esto significa que "&a" imprimirรก la direcciรณn de memoria de la variable a.
En este tutorial de Golang, ejecutaremos el siguiente programa para mostrar el valor de una variable y la direcciรณn de esa variable.
package main
import "fmt"
func main() {
a := 20
fmt.Println("Address:",&a)
fmt.Println("Value:",a)
}
El resultado serรก
Address: 0xc000078008 Value: 20
Una variable de puntero almacena la direcciรณn de memoria de otra variable. Puede definir un puntero usando la sintaxis
var variable_name *type
El asterisco (*) representa que la variable es un puntero. Comprenderรก mรกs ejecutando el siguiente programa.
package main
import "fmt"
func main() {
//Create an integer variable a with value 20
a := 20
//Create a pointer variable b and assigned the address of a
var b *int = &a
//print address of a(&a) and value of a
fmt.Println("Address of a:",&a)
fmt.Println("Value of a:",a)
//print b which contains the memory address of a i.e. &a
fmt.Println("Address of pointer b:",b)
//*b prints the value in memory address which b contains i.e. the value of a
fmt.Println("Value of pointer b",*b)
//increment the value of variable a using the variable b
*b = *b+1
//prints the new value using a and *b
fmt.Println("Value of pointer b",*b)
fmt.Println("Value of a:",a)}
La salida serรก
Address of a: 0x416020 Value of a: 20 Address of pointer b: 0x416020 Value of pointer b 20 Value of pointer b 21 Value of a: 21
Estructuras
Una estructura es un tipo de datos definido por el usuario que a su vez contiene un elemento mรกs del mismo o diferente tipo.
Usar una estructura es un proceso de 2 pasos.
Primero, cree (declare) un tipo de estructura.
En segundo lugar, cree variables de ese tipo para almacenar valores.
Las estructuras se utilizan principalmente cuando se desea almacenar datos relacionados juntos.
Considere una informaciรณn del empleado que tenga nombre, edad y direcciรณn. Puedes manejar esto de 2 maneras.
Cree 3 matrices: una matriz almacena los nombres de los empleados, otra almacena la edad y la tercera almacena la edad.
Declare un tipo de estructura con 3 campos: nombre, direcciรณn y edad. Cree una matriz de ese tipo de estructura donde cada elemento sea un objeto de estructura que tenga nombre, direcciรณn y edad.
El primer enfoque no es eficiente. En este tipo de escenarios, las estructuras son mรกs convenientes.
La sintaxis para declarar una estructura es
type structname struct {
variable_1 variable_1_type
variable_2 variable_2_type
variable_n variable_n_type
}
Un ejemplo de declaraciรณn de estructura es
type emp struct {
name string
address string
age int
}
Aquรญ se crea un nuevo tipo definido por el usuario llamado emp. Ahora puedes crear variables del tipo emp usando la sintaxis
var variable_name struct_name
Un ejemplo es
var empdata1 emp
Puede establecer valores para empdata1 como
empdata1.name = "John" empdata1.address = "Street-1, Bangalore" empdata1.age = 30
Tambiรฉn puede crear una variable de estructura y asignar valores mediante
empdata2 := emp{"Raj", "Building-1, Delhi", 25}
Aquรญ es necesario mantener el orden de los elementos. Raj se asignarรก al nombre, el siguiente elemento a la direcciรณn y el รบltimo a la edad.
Ejecute el siguiente cรณdigo
package main
import "fmt"
//declared the structure named emp
type emp struct {
name string
address string
age int
}
//function which accepts variable of emp type and prints name property
func display(e emp) {
fmt.Println(e.name)
}
func main() {
// declares a variable, empdata1, of the type emp
var empdata1 emp
//assign values to members of empdata1
empdata1.name = "John"
empdata1.address = "Street-1, London"
empdata1.age = 30
//declares and assign values to variable empdata2 of type emp
empdata2 := emp{"Raj", "Building-1, Paris", 25}
//prints the member name of empdata1 and empdata2 using display function
display(empdata1)
display(empdata2)
}
Salida
John Raj
Mรฉtodos (no funciones)
Un mรฉtodo es una funciรณn con un argumento receptor. ArchiTรฉcnicamente, estรก entre la palabra clave func y el nombre del mรฉtodo. La sintaxis de un mรฉtodo es
func (variable variabletype) methodName(parameter1 paramether1type) {
}
Convirtamos el programa de ejemplo anterior para usar mรฉtodos en lugar de funciones.
package main
import "fmt"
//declared the structure named emp
type emp struct {
name string
address string
age int
}
//Declaring a function with receiver of the type emp
func(e emp) display() {
fmt.Println(e.name)
}
func main() {
//declaring a variable of type emp
var empdata1 emp
//Assign values to members
empdata1.name = "John"
empdata1.address = "Street-1, Lodon"
empdata1.age = 30
//declaring a variable of type emp and assign values to members
empdata2 := emp {
"Raj", "Building-1, Paris", 25}
//Invoking the method using the receiver of the type emp
// syntax is variable.methodname()
empdata1.display()
empdata2.display()
}
Go no es un lenguaje orientado a objetos y no tiene el concepto de clase. Los mรฉtodos dan una idea de lo que se hace en los programas orientados a objetos donde las funciones de una clase se invocan usando la sintaxis nombreobjeto.nombrefunciรณn()
Concurrencia
Go admite la ejecuciรณn simultรกnea de tareas, es decir, puede ejecutar varias tareas simultรกneamente. Es diferente del concepto de paralelismo. En el paralelismo, una tarea se divide en pequeรฑas subtareas y se ejecutan en paralelo, pero en la concurrencia, se ejecutan varias tareas simultรกneamente. La concurrencia se logra en Go mediante Goroutines y Canales.
gorutinas
Una gorutina es una funciรณn que puede ejecutarse simultรกneamente con otras funciones. Por lo general, cuando se invoca una funciรณn, el control se transfiere a la funciรณn llamada y, una vez completada la ejecuciรณn, el control regresa a la funciรณn que llama. La funciรณn que llama luego continรบa su ejecuciรณn. La funciรณn que llama espera a que la funciรณn invocada complete la ejecuciรณn antes de continuar con el resto de las declaraciones.
Pero en el caso de goroutine, la funciรณn que llama no esperarรก a que se complete la ejecuciรณn de la funciรณn invocada. Continuarรก ejecutรกndose con las siguientes declaraciones. Puede tener varias gorutinas en un programa.
Ademรกs, el programa principal saldrรก una vez que complete la ejecuciรณn de sus declaraciones y no esperarรก a que se completen las rutinas invocadas.
Goroutine se invoca utilizando la palabra clave go seguida de una llamada a funciรณn.
Ejemplo
go add(x,y)
Comprenderรก las gorutinas con los siguientes ejemplos de Golang. Ejecute el siguiente programa
package main
import "fmt"
func display() {
for i:=0; i<5; i++ {
fmt.Println("In display")
}
}
func main() {
//invoking the goroutine display()
go display()
//The main() continues without waiting for display()
for i:=0; i<5; i++ {
fmt.Println("In main")
}
}
La salida serรก
In main In main In main In main In main
Aquรญ el programa principal completรณ la ejecuciรณn incluso antes de que comenzara la rutina. display() es una rutina que se invoca usando la sintaxis
go function_name(parameter list)
En el cรณdigo anterior, main() no espera a que se complete display(), y main() completรณ su ejecuciรณn antes de que display() ejecutara su cรณdigo. Entonces la declaraciรณn de impresiรณn dentro de display() no se imprimiรณ.
Ahora modificamos el programa para imprimir tambiรฉn las declaraciones desde display(). Agregamos un retraso de 2 segundos en el bucle for de main() y un retraso de 1 segundo en el bucle for de display().
package main
import "fmt"
import "time"
func display() {
for i:=0; i<5; i++ {
time.Sleep(1 * time.Second)
fmt.Println("In display")
}
}
func main() {
//invoking the goroutine display()
go display()
for i:=0; i<5; i++ {
time.Sleep(2 * time.Second)
fmt.Println("In main")
}
}
La salida serรก algo similar a
In display In main In display In display In main In display In display In main In main In main
Aquรญ puede ver que ambos bucles se ejecutan de forma superpuesta debido a la ejecuciรณn simultรกnea.
Canales
Los canales son una forma para que las funciones se comuniquen entre sรญ. Se puede pensar como un medio donde una rutina coloca los datos y otra rutina accede a ellos en el servidor Golang.
Un canal se puede declarar con la sintaxis
channel_variable := make(chan datatype)
Ejemplo:
ch := make(chan int)
Puedes enviar datos a un canal usando la sintaxis
channel_variable <- variable_name
Ejemplo
ch <- x
Puede recibir datos de un canal usando la sintaxis
variable_name := <- channel_variable
Ejemplo
y := <- ch
En los ejemplos de goroutine del lenguaje Go anteriores, ha visto que el programa principal no espera a la goroutine. Pero ese no es el caso cuando se trata de canales. Supongamos que si una gorutina envรญa datos al canal, main() esperarรก la declaraciรณn que recibe los datos del canal hasta que obtenga los datos.
Verรก esto en los siguientes ejemplos de lenguaje Go. Primero, escriba una rutina normal y observe el comportamiento. Luego modifique el programa para usar canales y vea el comportamiento.
Ejecute el siguiente programa
package main
import "fmt"
import "time"
func display() {
time.Sleep(5 * time.Second)
fmt.Println("Inside display()")
}
func main() {
go display()
fmt.Println("Inside main()")
}
La salida serรก
Inside main()
main() finalizรณ la ejecuciรณn y saliรณ antes de que se ejecutara la rutina. Entonces la impresiรณn dentro de display() no se ejecutรณ.
Ahora modifique el programa anterior para usar canales y ver el comportamiento.
package main
import "fmt"
import "time"
func display(ch chan int) {
time.Sleep(5 * time.Second)
fmt.Println("Inside display()")
ch <- 1234
}
func main() {
ch := make(chan int)
go display(ch)
x := <-ch
fmt.Println("Inside main()")
fmt.Println("Printing x in main() after taking from channel:",x)
}
La salida serรก
Inside display() Inside main() Printing x in main() after taking from channel: 1234
Aquรญ lo que sucede es que main() al llegar a x := <-ch esperarรก datos en el canal ch. El display() tiene una espera de 5 segundos y luego envรญa datos al canal ch. El main() al recibir los datos del canal se desbloquea y continรบa su ejecuciรณn.
El remitente que envรญa datos al canal puede informar a los receptores que no se agregarรกn mรกs datos al canal al cerrarlo. Esto se utiliza principalmente cuando se utiliza un bucle para enviar datos a un canal. Un canal se puede cerrar usando
close(channel_name)
Y en el extremo del receptor, es posible verificar si el canal estรก cerrado usando una variable adicional mientras se obtienen datos del canal usando
variable_name, status := <- channel_variable
Si el estado es Verdadero, significa que recibiรณ datos del canal. Si es falso, significa que estรกs intentando leer desde un canal cerrado.
Tambiรฉn puede utilizar canales para la comunicaciรณn entre gorutinas. Es necesario utilizar 2 gorutinas: una envรญa datos al canal y la otra recibe los datos del canal. Vea el programa a continuaciรณn
package main
import "fmt"
import "time"
//This subroutine pushes numbers 0 to 9 to the channel and closes the channel
func add_to_channel(ch chan int) {
fmt.Println("Send data")
for i:=0; i<10; i++ {
ch <- i //pushing data to channel
}
close(ch) //closing the channel
}
//This subroutine fetches data from the channel and prints it.
func fetch_from_channel(ch chan int) {
fmt.Println("Read data")
for {
//fetch data from channel
x, flag := <- ch
//flag is true if data is received from the channel
//flag is false when the channel is closed
if flag == true {
fmt.Println(x)
}else{
fmt.Println("Empty channel")
break
}
}
}
func main() {
//creating a channel variable to transport integer values
ch := make(chan int)
//invoking the subroutines to add and fetch from the channel
//These routines execute simultaneously
go add_to_channel(ch)
go fetch_from_channel(ch)
//delay is to prevent the exiting of main() before goroutines finish
time.Sleep(5 * time.Second)
fmt.Println("Inside main()")
}
Aquรญ hay 2 subrutinas, una envรญa datos al canal y la otra los imprime. La funciรณn add_to_channel suma los nรบmeros del 0 al 9 y cierra el canal. Simultรกneamente, la funciรณn fetch_from_channel espera
x, flag := <- ch y una vez que los datos estรฉn disponibles, los imprime. Sale una vez que la bandera es falsa, lo que significa que el canal estรก cerrado.
La espera en main() se da para evitar la salida de main() hasta que las gorutinas finalicen la ejecuciรณn.
Ejecute el cรณdigo y vea el resultado como
Read data Send data 0 1 2 3 4 5 6 7 8 9 Empty channel Inside main()
Seleccione
Seleccionar puede verse como una declaraciรณn de cambio que funciona en canales. Aquรญ las declaraciones del caso serรกn una operaciรณn de canal. Por lo general, las declaraciones de cada caso se leerรกn desde el canal. Cuando cualquiera de los casos estรก listo (se lee el canal), se ejecuta la declaraciรณn asociada con ese caso. Si hay varios casos listos, elegirรก uno al azar. Puede tener un caso predeterminado que se ejecute si ninguno de los casos estรก listo.
Veamos el siguiente cรณdigo.
package main
import "fmt"
import "time"
//push data to channel with a 4 second delay
func data1(ch chan string) {
time.Sleep(4 * time.Second)
ch <- "from data1()"
}
//push data to channel with a 2 second delay
func data2(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "from data2()"
}
func main() {
//creating channel variables for transporting string values
chan1 := make(chan string)
chan2 := make(chan string)
//invoking the subroutines with channel variables
go data1(chan1)
go data2(chan2)
//Both case statements wait for data in the chan1 or chan2.
//chan2 gets data first since the delay is only 2 sec in data2().
//So the second case will execute and exits the select block
select {
case x := <-chan1:
fmt.Println(x)
case y := <-chan2:
fmt.Println(y)
}
}
La ejecuciรณn del programa anterior darรก el resultado:
from data2()
Aquรญ la declaraciรณn de selecciรณn espera a que los datos estรฉn disponibles en cualquiera de los canales. data2() agrega datos al canal despuรฉs de un perรญodo de suspensiรณn de 2 segundos, lo que harรก que se ejecute el segundo caso.
Agregue un caso predeterminado a la selecciรณn en el mismo programa y vea el resultado. Aquรญ, al llegar al bloque de selecciรณn, si ningรบn caso tiene datos listos en el canal, ejecutarรก el bloque predeterminado sin esperar a que los datos estรฉn disponibles en ningรบn canal.
package main
import "fmt"
import "time"
//push data to channel with a 4 second delay
func data1(ch chan string) {
time.Sleep(4 * time.Second)
ch <- "from data1()"
}
//push data to channel with a 2 second delay
func data2(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "from data2()"
}
func main() {
//creating channel variables for transporting string values
chan1 := make(chan string)
chan2 := make(chan string)
//invoking the subroutines with channel variables
go data1(chan1)
go data2(chan2)
//Both case statements check for data in chan1 or chan2.
//But data is not available (both routines have a delay of 2 and 4 sec)
//So the default block will be executed without waiting for data in channels.
select {
case x := <-chan1:
fmt.Println(x)
case y := <-chan2:
fmt.Println(y)
default:
fmt.Println("Default case executed")
}
}
Este programa darรก el resultado:
Default case executed
Esto se debe a que cuando llegรณ el bloque de selecciรณn, ningรบn canal tenรญa datos para leer. Entonces, se ejecuta el caso predeterminado.
Mutex
Mutex es la forma abreviada de exclusiรณn mutua. Mutex se utiliza cuando no se desea permitir que varias subrutinas accedan a un recurso al mismo tiempo. Mutex tiene dos mรฉtodos: bloquear y desbloquear. Mutex estรก incluido en el paquete sync, por lo que hay que importar el paquete sync. Las instrucciones que se deben ejecutar de forma mutuamente exclusiva se pueden colocar dentro de mutex.Lock() y mutex.Unlock().
Aprendamos mutex con un ejemplo que cuenta el nรบmero de veces que se ejecuta un bucle. En este programa esperamos que la rutina ejecute el bucle 10 veces y el recuento se almacene en suma. Llamas a esta rutina 3 veces, por lo que el recuento total debe ser 30. El recuento se almacena en una variable global.
Primero, ejecuta el programa sin mutex.
package main
import "fmt"
import "time"
import "strconv"
import "math/rand"
//declare count variable, which is accessed by all the routine instances
var count = 0
//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
//loop incrementing the count by 10
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
temp := count
temp++
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
count = temp
}
fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}
func main() {
//loop calling the process() 3 times
for i := 1; i < 4; i++ {
go process(i)
}
//delay to wait for the routines to complete
time.Sleep(25 * time.Second)
fmt.Println("Final Count:", count)
}
Ver el resultado
Count after i=1 Count: 11 Count after i=3 Count: 12 Count after i=2 Count: 13 Final Count: 13
El resultado podrรญa ser diferente cuando lo ejecutes pero el resultado final no serรก 30.
Aquรญ lo que sucede es que 3 gorutinas intentan aumentar el recuento de bucles almacenado en la variable recuento. Supongamos que en un momento el recuento es 5 y goroutine1 va a incrementar el recuento a 6. Los pasos principales incluyen
Copiar recuento a temperatura
Incrementar temperatura
Almacenar la temperatura nuevamente para contar
Supongamos que poco despuรฉs de realizar el paso 3 de goroutine1; otra gorutina podrรญa tener un valor antiguo, digamos que 3 realiza los pasos anteriores y almacena 4 nuevamente, lo cual es incorrecto. Esto se puede evitar usando mutex, que hace que otras rutinas esperen cuando una rutina ya estรก usando la variable.
Ahora ejecutarรก el programa con mutex. Aquรญ los 3 pasos mencionados anteriormente se ejecutan en un mutex.
package main
import "fmt"
import "time"
import "sync"
import "strconv"
import "math/rand"
//declare a mutex instance
var mu sync.Mutex
//declare count variable, which is accessed by all the routine instances
var count = 0
//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
//loop incrementing the count by 10
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
//lock starts here
mu.Lock()
temp := count
temp++
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
count = temp
//lock ends here
mu.Unlock()
}
fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}
func main() {
//loop calling the process() 3 times
for i := 1; i < 4; i++ {
go process(i)
}
//delay to wait for the routines to complete
time.Sleep(25 * time.Second)
fmt.Println("Final Count:", count)
}
Ahora la salida serรก
Count after i=3 Count: 21 Count after i=2 Count: 28 Count after i=1 Count: 30 Final Count: 30
Aquรญ obtenemos el resultado esperado como resultado final. Porque las declaraciones de lectura, incremento y reescritura del recuento se ejecutan en un mutex.
Manejo de errores
Los errores son condiciones anormales como cerrar un archivo que no estรก abierto, abrir un archivo que no existe, etc. Las funciones generalmente devuelven errores como รบltimo valor de retorno.
El siguiente ejemplo explica mรกs sobre el error.
package main
import "fmt"
import "os"
//function accepts a filename and tries to open it.
func fileopen(name string) {
f, er := os.Open(name)
//er will be nil if the file exists else it returns an error object
if er != nil {
fmt.Println(er)
return
}else{
fmt.Println("file opened", f.Name())
}
}
func main() {
fileopen("invalid.txt")
}
La salida serรก:
open /invalid.txt: no such file or directory
Aquรญ intentamos abrir un archivo que no existe y devolviรณ el error a la variable er. Si el archivo es vรกlido, entonces el error serรก nulo.
Errores personalizados
Con esta funciรณn, puede crear errores personalizados. Esto se hace usando New() del paquete de error. Reescribiremos el programa anterior para utilizar errores personalizados.
Ejecute el siguiente programa
package main
import "fmt"
import "os"
import "errors"
//function accepts a filename and tries to open it.
func fileopen(name string) (string, error) {
f, er := os.Open(name)
//er will be nil if the file exists else it returns an error object
if er != nil {
//created a new error object and returns it
return "", errors.New("Custom error message: File name is wrong")
}else{
return f.Name(),nil
}
}
func main() {
//receives custom error or nil after trying to open the file
filename, error := fileopen("invalid.txt")
if error != nil {
fmt.Println(error)
}else{
fmt.Println("file opened", filename)
}
}
La salida serรก:
Custom error message:File name is wrong
Aquรญ el รกrea() devuelve el รกrea de un cuadrado. Si la entrada es menor que 1, entonces area() devuelve un mensaje de error.
Leer archivos
Los archivos se utilizan para almacenar datos. Go nos permite leer datos de los archivos
Primero cree un archivo, data.txt, en su directorio actual con el siguiente contenido.
Line one Line two Line three
Ahora ejecute el siguiente programa para ver cรณmo imprime el contenido del archivo completo como salida.
package main
import "fmt"
import "io/ioutil"
func main() {
data, err := ioutil.ReadFile("data.txt")
if err != nil {
fmt.Println("File reading error", err)
return
}
fmt.Println("Contents of file:", string(data))
}
Aquรญ los datos, err := ioutil.ReadFile(โdata.txtโ) lee los datos y devuelve una secuencia de bytes. Mientras se imprime, se convierte al formato de cadena.
Escribir archivos
Verรกs esto con un programa.
package main
import "fmt"
import "os"
func main() {
f, err := os.Create("file1.txt")
if err != nil {
fmt.Println(err)
return
}
l, err := f.WriteString("Write Line one")
if err != nil {
fmt.Println(err)
f.Close()
return
}
fmt.Println(l, "bytes written")
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
Aquรญ se crea un archivo, test.txt. Si el archivo ya existe, su contenido se trunca. Writeline() se utiliza para escribir el contenido del archivo. Despuรฉs de eso, cerrรณ el archivo usando Close().
Hoja de trucos
En este tutorial de Go, cubrimos,
| Tema | Descripciรณn | Sintaxis |
|---|---|---|
| Tipos basicos | Numรฉrico, cadena, bool | |
| Variables | Declarar y asignar valores a variables. | var tipo nombre_variable var nombre_variable tipo = valor var nombre_variable1, nombre_variable2 = valor1, valor2 nombre_variable := valor |
| Constantes | Variables cuyo valor no se puede cambiar una vez asignado | variable constante = valor |
| En bucle | Ejecutar declaraciones en un bucle. | para inicializaciรณn_expresiรณn; expresiรณn_evaluaciรณn; iteraciรณn_expresiรณn{ // una o mรกs declaraciones } |
| Si mas | Es una declaraciรณn condicional. | si condiciรณn { // declaraciones_1 Else {} // declaraciones_2 } |
| cambiar | Declaraciรณn condicional con mรบltiples casos. | cambiar de expresiรณn { valor de caso_1: declaraciones_1 valor de caso_2: declaraciones_2 valor de caso_n: declaraciones_n por defecto: declaraciones_predeterminadas } |
| Formaciรณn | Secuencia con nombre de tamaรฑo fijo de elementos del mismo tipo | nombre de matriz: = [tamaรฑo] escriba {valor_0, valor_1,โฆ, valor_tamaรฑo-1} |
| Rebanada | Porciรณn o segmento de una matriz | var nombre_porciรณn [] tipo = nombre_matriz[inicio:fin] |
| Funciones | Bloque de declaraciones que realiza una tarea especรญfica. | func nombre_funciรณn (tipo parรกmetro_1, tipo parรกmetro_n) tipo_retorno { //declaraciones } |
| Paquetes | Se utilizan para organizar el cรณdigo. Aumenta la legibilidad y reutilizaciรณn del cรณdigo. | importar nombre_paquete |
| Aplazar | Difiere la ejecuciรณn de una funciรณn hasta que la funciรณn que la contiene finaliza su ejecuciรณn. | diferir nombre_funciรณn(lista_parรกmetros) |
| Punteros | Almacena la direcciรณn de memoria de otra variable. | var nombre_variable *tipo |
| Estructura | Tipo de datos definido por el usuario que a su vez contiene un elemento mรกs del mismo o diferente tipo | escriba nombre de estructura estructura { variable_1 variable_1_tipo variable_2 variable_2_tipo variable_n variable_n_tipo } |
| Mรฉtodos | Un mรฉtodo es una funciรณn con un argumento receptor. | func (variable tipo de variable) nombre_mรฉtodo(lista_parรกmetros) { } |
| gorutina | Una funciรณn que puede ejecutarse simultรกneamente con otras funciones. | ir nombre_funciรณn(lista_parรกmetros) |
| Channel | Manera de que las funciones se comuniquen entre sรญ. Medio en el que una rutina coloca datos y al que otra rutina accede. | Declarar: ch:= hacer(chan int) Enviar datos al canal: variable_canal <- nombre_variable Recibir del canal: nombre_variable := <- variable_canal |
| Seleccione | Declaraciรณn de cambio que funciona en canales. Las declaraciones del caso serรกn una operaciรณn de canal. Cuando cualquiera de los canales estรก listo con datos, se ejecuta la declaraciรณn asociada con ese caso. | seleccionar { caso x := <-chan1: fmt.Println(x) caso y := <-chan2: fmt.Println(y) } |
| Mutex | Mutex se utiliza cuando no se desea permitir que varias subrutinas accedan a un recurso al mismo tiempo. Mutex tiene 2 mรฉtodos: bloquear y desbloquear | mutex.Lock() //declaraciones mutex.Desbloquear(). |
| Leer archivos | Lee los datos y devuelve una secuencia de bytes. | Datos, err := ioutil.ReadFile(nombre de archivo) |
| Escribir archivo | Escribe datos en un archivo. | l, errar := f.WriteString(text_to_write) |
