A veces el valor cero no es suficiente y es necesario un constructor de inicialización, como en este ejemplo derivado del paquete os.
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
Hay mucho texto en este codigo que podemos simplificarlo usando un literal compuesto, que es una expresión que crea una nueva instancia:
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := File{fd, name, nil, 0}
return &f
}
Tenemos que tener en cuenta que, a diferencia de C, está perfectamente bien devolver la dirección de una variable local; el almacenamiento asociado con la variable sobrevive después de que regresa la función. De hecho, al tomar la dirección de un literal compuesto se asigna una instancia nueva cada vez que se evalúa, por lo que podemos combinar estas dos últimas líneas:
return &File{fd, name, nil, 0}
Los campos de un literal compuesto están ordenados y todos deben estar presentes. Sin embargo, al etiquetar los elementos explícitamente como pares campo:valor, los inicializadores pueden aparecer en cualquier orden, dejando los que faltan con sus respectivos valores cero. Así podríamos decir
return &File{fd: fd, name: name}
Como caso límite, si un literal compuesto no contiene ningún campo, crea un valor cero para el tipo. Las expresiones new(File) y &File{} son equivalentes.
También se pueden crear literales compuestos para matrices, slices y mapas, siendo las etiquetas de campo índices o claves de mapa, según corresponda. En estos ejemplos, las inicializaciones funcionan independientemente de los valores de Enone, Eio y Einval, siempre que sean distintos.
a := [...]string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
s := []string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}