Personalizar la salida en la consola es crucial para que nuestro código sea más legible y profesional. En los lenguajes de programación, tener una salida por consola bien estructurada es esencial para facilitar la comprensión y el mantenimiento del código. En el entorno del lenguaje Go, personalizar la salida de estructuras (structs) es una tarea sencilla y eficaz mediante el uso de una función que implemente el método de cadena (string).
¿Qué es un struct en Go?
Un struct en Go es una colección de campos de datos. Es similar a una clase en otros lenguajes de programación, pero sin funcionalidades como la herencia. El uso de structs permite organizar datos relacionados y trabajar con ellos de manera eficiente.
Por ejemplo, consideremos un struct PC que modela un ordenador con información sobre la RAM, el disco y el fabricante. Este struct podría definirse de la siguiente manera:
type PC struct{ RAM int Brand string Disk int}
¿Cómo crear una función string para un struct?
El lenguaje Go permite definir métodos en estructuras. Para personalizar el output de un struct, se puede implementar el método String(). Este método debe adherirse a unas convenciones específicas para asegurar el formato correcto.
A continuación, se muestra cómo implementar una función String() para nuestro struct PC:
func(miPC PC)String()string{return fmt.Sprintf("Tengo %d gigabytes de RAM, %d gigabytes de disco y es una %s.", miPC.RAM, miPC.Disk, miPC.Brand)}
En este código:
Implementamos el método String() que retorna un string personalizado.
Utilizamos la función fmt.Sprintf() para construir el string de salida.
Especificamos patrones como %d y %s dentro de la cadena para formatear enteros y strings, respectivamente.
¿Cuál es el procedimiento para imprimir el struct en la consola?
Una vez implementado el método String(), el struct puede imprimirse directamente con un fmt.Printf() o fmt.Println(), y automáticamente se utilizará el formato personalizado que definimos. Aquí hay un ejemplo:
Tengo16 gigabytes de RAM,100 gigabytes de disco y es una MSI.
¿Por qué es eficiente esta técnica?
Esta técnica es eficiente porque encapsula la lógica de formateo dentro del struct, eliminando la necesidad de definir cómo se debe imprimir cada instancia. Permite mejorar la mantenibilidad y legibilidad del código. Cuando se trabaja a gran escala o se desarrollan interfaces de usuario complejas, la programación de este tipo de métodos es una práctica recomendable en términos de eficiencia y claridad.
Recomendaciones adicionales
Consistencia: Asegúrate de que todos los structs relevantes tengan implementaciones del método String() si deseas obtener una salida consistente.
Legibilidad: Mantén el formato de salida claro y alineado con las necesidades de los usuarios finales o los desarrolladores que interactuarán con el código.
Documentación: Añade comentarios siempre que sea necesario para explicar la lógica detrás del método de formateo, especialmente cuando el formato es complejo.
Esta personalización te ayudará a crear aplicaciones más amigables y robustas, permitiéndote abordar desafíos de manera eficaz. ¡Continúa explorando las funcionalidades de Go y aplica este conocimiento en tus futuros proyectos!
La estructura de datos " Struct " tiene un método llamado " String " , que podemos sobrescribir para personalizar la salida a consola de los datos del struct.
Muy interesante aporte, gracias :)
Buen resumen de la lección, me ayudo a entender mejor que se trataba de un método de un Struct. Lo cual no entendí hasta leer tu comentario.
Por lo que puedo ver, lo que va entre func y el nombre de la funcion (miPC pc) es lo que asocia a la funcion con la struct, pero no necesariamente con el "objeto" miPC, ahi puede ir cualquier nombre. Ej:
// Stringerfunc(juanito pc)String() string {return fmt.Sprintf("PC: %s con %dGB RAM y SSD %dGB", juanito.brand, juanito.ram, juanito.disk)}
y por otro lado, note algo que esta bueno me parece. Como el struct se inicializa con pares clave:valor entre llaves {}, no hace falta que esten en orden, se lo cambie y funciona igual:
type pc struct { ram int
disk int
brand string
}...// con la structmiPC:= pc{ram:16,brand:"Dell",disk:480} fmt.Println(miPC)// llama a String
Es correcto yo lo entendi como una instacia temporal de la clase para poder acceder a sus atributos... Algo asi como el this. en otros lenguajes de programacion.
Creo que los stringers es muy parecido a lo que Python tiene para las clases que se llama el método __src__
__str__
o al toString() de java
Hay otra manera:
fmt.Printf("%+v", myPC)
Escribe los campos y los valores, es bastante legible sin sobrescribir el método String
con “%#v” entrega un resultado mas explicito 😉
Excelente aporte, compañero
Este es mi codigo de esta clase 👇
package main
import"fmt"type pc struct { ram int
os string
disk int
}func(myPC pc)String() string {return fmt.Sprintf("Tengo %d GB de RAM, %d GB de Disco y es un sistema %s.", myPC.ram, myPC.disk, myPC.os)}func main(){myPC:= pc{ram:16,os:"Linux",disk:100} fmt.Println(myPC)}
package main
import"fmt"type pc struct { tarjetagrafica string
ram int
disk int
}//Esta funcion trabajara sobre la variable y la struct definidas en su principio.//Luego se escribira la funcion "String" y despues el tipo de dato que retornara.func(miPc pc)String() string {return fmt.Sprintf("Tengo %dGB de RAM, %dGB de disco y una targeta grafica %s", miPc.ram, miPc.disk, miPc.tarjetagrafica)}//con el metodo stringers podremos modificar los output's de las structs en consola.func main(){miPc:= pc{ram:16,tarjetagrafica:"3080ti",disk:1000} fmt.Println(miPc)//De esta manera personalizaremos nuestro output en consola.}
todo esto me recueda a C jaja y viejo y traumatico C o C++ jaja
Los stringers son parecidos al metódo toString() de Java ☕
Uff!! El famoso toString() de Java ❣️
Pense lo mismo pero que bueno que aclaraste mi sospecha xD
no entiendo bien cmo usa los %d %s osea por que se introduce la variable asi ?
Son como representaciones de variables y van según al tipo, aquí puedes ver más información
package car
import"encoding/json"// Modelo Publico de Cartype CarPublic struct {Brand string
Model string
Year int
Seats int
}func(c *CarPublic)ToMap()(res map[string]interface{}){ a,err:= json.Marshal(c)if err != nil {panic(err)} json.Unmarshal(a,&res)return}
Lo que hice fue buscar la manera de convertir el struct en un map. Este método es agnóstico a la estructura del struct, por lo que se puede aplicar a cualquiera sin tener que hardcodear los atributos.
Posteriormente, definí en un paquete aparte llamado utils, un método llamado PrintStruct que imprime cada par key:value por línea.
func PrintStruct(f map[string]interface{}){for i,value:= range f { fmt.Printf("%s: %v\n", i, value)}}
Si en el editor les sale que es mala practica mezclar valor y puntero y quieres hacerlo con puntero, es de esta forma
package main
import"fmt"type pc struct { ram int
disk int
brand string
}func(myPC *pc)String() string {return fmt.Sprintf("RAM: %d, Disk: %d, Brand: %s", myPC.ram, myPC.disk, myPC.brand)}func main(){myPC:= pc{ram:16,disk:512,brand:"Lenovo"} fmt.Println(&myPC)}
Code Resume
package main
import("curso_golang_platzi/src/mypackage""fmt")funcmain(){var myPc mypackage.Pc
myPc.SetBrand("MSI") myPc.SetRam(16) myPc.SetDisk(1000) fmt.Println(myPc)}
package mypackage
import"fmt"type Pc struct{ brand string ram uint disk uint}func(p Pc)GetBrand()string{return p.brand
}func(p *Pc)SetBrand(brand string){ p.brand = brand
}func(p Pc)GetRam()uint{return p.ram
}func(p *Pc)SetRam(ram uint){ p.ram = ram
}func(p Pc)GetDisk()uint{return p.disk
}func(p *Pc)SetDisk(disk uint){ p.disk = disk
}//Stringers//This way we overload the print function for our structfunc(p Pc)String()string{return fmt.Sprintf("RAM: %d GB, DISK: %d GB, BRAND: %s", p.ram, p.disk, p.brand)}funcmypackage(){}
package main
import"fmt"type computer struct { ram int
brand string
disk int
}func(myComputer computer)String() string { fmt.Println() fmt.Println("=========================================================================") fmt.Println("... entrando a función sobrescrita de representación string del struct...") fmt.Println("-------------------------------------------------------------------------")result:= fmt.Sprintf("CantidadRam: %d GB, cantidadHD: %d GB, marca: %s", myComputer.ram, myComputer.disk, myComputer.brand) fmt.Println("-------------------------------------------------------------------------") fmt.Println("... Saliendo a función sobrescrita de representación string del struct...") fmt.Println("_________________________________________________________________________") fmt.Println()return result
}func main(){ fmt.Println() fmt.Println("============================================================================================") fmt.Println("Sobrescribiendo funciones propias de objetos") fmt.Println() fmt.Println("... ejemplo usando Stringers para cadenas de texto.") fmt.Println("============================================================================================") fmt.Println()myComputer:= computer{ram:16,brand:"msi",disk:100} fmt.Println("Imprimiendo con 'fmt' el struct con su funcion 'string' sobrescrita:") fmt.Println("Este es el struct >>>", myComputer) fmt.Println()}
// [...]type Pc struct{ Ram, Disk int Brand string}func(myPc Pc)String()string{return fmt.Sprintf("1.RAM: %dGB\n2.DISK: %dGB \n3.BRAND: %s", myPc.Ram, myPc.Disk, myPc.Brand)}
Interesante
Los stringers nos sirven para modificar la forma en la que se imprime un struct, se hacen de la siguiente manera:
package main
type pc struct{ ram int model string}func(myPc pc)String()string{return fmt.Sprintf("Es un %s model, y tiene %d RAM")}funcmain(){ myPc := pc{model:"Asus", ram:16} fmt.Println(myPc)}
El output de el Println sera de esta forma:
$ go run main.go
Es un Asus model, y tiene 16 RAM
Si no hubieramos creado el stringer el output seria: