Sintaxis de declaración de función: cosas entre paréntesis antes del nombre de la función
Lamento no poder ser más específico en el título de la pregunta, pero estaba leyendo código Go y encontré declaraciones de funciones de esta forma:
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
}
de https://github.com/mattermost/platform/blob/master/api/context.go
func (s *GracefulServer) BlockingClose() bool {
...
}
de https://github.com/braintree/manners/blob/master/server.go
¿ Qué significa el (h handler)
y el (s *GracefulServer)
entre paréntesis? ¿Qué significa toda la declaración de función, teniendo en cuenta el significado de las cosas entre paréntesis?
Editar
Esto no es un duplicado de ¿Cuál es la diferencia entre funciones y métodos en Go? : esta pregunta me surgió porque no sabía cuáles eran las cosas entre paréntesis antes del nombre de la función, no porque me preguntara cuál era la diferencia entre funciones y métodos... si supiera que esta declaración es un método, lo haría En primer lugar, no he tenido esta pregunta. Si alguien tiene la misma duda que yo algún día, no creo que busque "métodos golang" porque no sabe que ese es el caso. Sería como preguntarse qué significa la letra "sigma" delante de una expresión matemática (sin saber que significa suma) y alguien dice que es un duplicado de cuál es la diferencia entre suma y alguna otra cosa.
Además, la respuesta corta a esta pregunta ("es un receptor") no es una respuesta a "cuál es la diferencia entre funciones y métodos".
Esto se llama "receptor". En el primer caso (h handler)
es un tipo de valor, en el segundo (s *GracefulServer)
es un puntero. La forma en que esto funciona en Go puede variar un poco con respecto a otros idiomas. El tipo receptor, sin embargo, funciona más o menos como una clase en la mayoría de la programación orientada a objetos. Es desde donde llamas al método, muy parecido a si pongo algún método A
dentro de alguna clase Person
, entonces necesitaría una instancia de tipo Person
para poder llamar A
(¡asumiendo que es un método de instancia y no estático!).
Un problema aquí es que el receptor se empuja a la pila de llamadas como otros argumentos, por lo que si el receptor es un tipo de valor, como en el caso de, handler
entonces estará trabajando en una copia de lo que llamó al método, lo que significa algo como h.Name = "Evan"
lo haría. no persiste después de regresar al alcance de la llamada. Por esta razón, cualquier cosa que espere cambiar el estado del receptor necesita usar un puntero o devolver el valor modificado (da más de un paradigma de tipo inmutable si está buscando eso).
Aquí está la sección relevante de la especificación; https://golang.org/ref/spec#Method_sets
Significa ServeHTTP
que no es una función independiente. El paréntesis antes del nombre de la función es la forma Go de definir el objeto en el que operarán estas funciones. Entonces, esencialmente ServeHTTP
es un método de controlador de tipo y se puede invocar usando cualquier objeto, digamos h, de controlador de tipo.
h.ServeHTTP(w, r)
También se les llama receptores. Hay dos formas de definirlos. Si desea modificar el receptor utilice un puntero como:
func (s *MyStruct) pointerMethod() { } // method on pointer
Si no necesita modificar el receptor, puede definirlo como un valor como:
func (s MyStruct) valueMethod() { } // method on value
Este ejemplo de Go Playground demuestra el concepto.
package main
import "fmt"
type Mutatable struct {
a int
b int
}
func (m Mutatable) StayTheSame() {
m.a = 5
m.b = 7
}
func (m *Mutatable) Mutate() {
m.a = 5
m.b = 7
}
func main() {
m := &Mutatable{0, 0}
fmt.Println(m)
m.StayTheSame()
fmt.Println(m)
m.Mutate()
fmt.Println(m)
El resultado del programa anterior es:
&{0 0}
&{0 0}
&{5 7}
Si está familiarizado con los métodos de extensión de C# ,
un método go ( una función con un argumento receptor especial ), por ejemplo
func (v Vertex) Abs() float64
es similar al método de extensión c#
static float Abs( this Vertex v);
Las diferencias entre tipos de valores y punteros se describen en la respuesta de Evanmcdonnal.