ProgramaciónDesarrollador Backend

¿Cómo se implementa el trabajo con el método String() en Go para tipos personalizados? ¿Cuándo se debe implementar y cómo pueden las características de la implementación afectar la salida de datos y los registros?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

Si un tipo personalizado implementa el método

String() string

entonces, al utilizar este valor en funciones fmt (por ejemplo, fmt.Println, fmt.Printf("%v"), el paquete log), se llama a este método para obtener la representación en cadena.

Es recomendable implementar String() para tipos personalizados que representan entidades, para obtener una salida legible y significativa en lugar de la estándar de fmt (%v). Se sugiere que la salida sea breve, adecuada al contexto de uso y, si es posible, segura (sin filtrar datos sensibles).

Ejemplo de implementación:

type Order struct { ID string Amount int } func (o Order) String() string { return fmt.Sprintf("Order[%s]: %d", o.ID, o.Amount) } func main() { o := Order{"A123", 99} fmt.Println(o) } // Salida: Order[A123]: 99

Si el método se implementa de manera incorrecta o inadecuada, esto lleva a registros con salidas sin sentido o peligrosas, dificultando la búsqueda de errores.

Pregunta capciosa

¿Qué sucede si tu tipo implementa el método String(), pero lo usas como puntero, no como valor? ¿Se llamará String()?

Respuesta: Se llamará al método String() si está implementado para el tipo-puntero. Si está implementado solo para el valor, pero en el código el tipo es un puntero, el método se invocará gracias a la desreferenciación automática de Go para métodos sin receptor de puntero. Normalmente no es un problema, pero si el método está implementado solo para el receptor de puntero y lo llamas para un valor, entonces habrá un error de compilación.

Ejemplo:

type X struct{} func (x *X) String() string { return "ptr" } fmt.Println(X{}) // error: X no implementa String fmt.Println(&X{}) // ok, se llamará al método

Ejemplos de errores reales debido a la ignorancia de los matices del tema


Historia

En un proyecto grande, el tipo de error implementó String() solo en el receptor de puntero (func (err *MyErr) String() string). Debido a esto, al devolver el valor del error (no un puntero) se mostraba en los registros {} en lugar de un mensaje útil. El error no fue detectado durante mucho tiempo.


Historia

Uno de los tipos almacenaba datos sensibles, y debido a una implementación incorrecta de String() (se mostraban los campos explícitamente), las contraseñas de los usuarios comenzaron a aparecer en los registros de producción. Se necesitó una auditoría de seguridad.


Historia

El uso de la generación automática de String() llevó a que al actualizar la estructura, el nuevo formato de salida no coincidiera con el anterior, los registros se volvieron ilegibles y se interrumpió el análisis de registros por sistemas externos.