J'apprends Go en écrivant une application pour GAE, et c'est la signature d'une fonction de gestionnaire:
func handle(w http.ResponseWriter, r *http.Request) {}
Je suis un pointeur novice ici, alors pourquoi l’objet Request
est-il un pointeur alors que ResponseWriter
ne l’est pas? Est-il nécessaire de l’avoir de cette façon ou s’agit-il simplement de rendre possible une sorte de code avancé basé sur un pointeur?
Ce que vous obtenez pour w
est un pointeur sur le type non exporté http.response
mais comme ResponseWriter
est une interface, ce n'est pas visible.
De server.go :
type ResponseWriter interface {
...
}
Par contre, r
est un pointeur sur une structure concrète, d'où la nécessité de la préciser explicitement:
De request.go :
type Request struct {
...
}
Le http.ResponseWriter
est une interface et les types existants qui implémentent cette interface sont des pointeurs. Cela signifie qu'il n'est pas nécessaire d'utiliser un pointeur sur cette interface, car elle est déjà "soutenue" par un pointeur. Ce concept est décrit un peu par l’un des développeurs de go here Bien qu’un type implémentant http.ResponseWriter ne soit pas nécessairement un pointeur, il ne serait pas pratique, du moins pas dans le serveur http go.
http.Request
n'est pas une interface, c'est juste une structure, et puisque nous voulons changer cette structure et laisser le serveur Web voir ces modifications, il doit s'agir d'un pointeur. S'il ne s'agissait que d'une valeur de structure, nous en modifierions simplement une copie que le serveur Web appelant notre code ne pourrait pas voir.
Comme mentionné à juste titre dans de nombreuses autres réponses ici et ailleurs, ResponseWriter
est une interface et ses implications ont été décrites en détail dans les réponses et les blogs de SO.
Ce que je voudrais aborder, c’est ce que j’estime être la grande idée fausse, et dangereuse, que la requête de motif est transmise par "référence" (bien qu’une telle chose n’existe pas vraiment dans Go) est "nous voulons y apporter des modifications visibles sur le serveur".
Citant quelques réponses d'autres personnes:
[..] c'est juste une structure, et puisque nous voulons changer cette structure et que le serveur Web voie ces modifications, il doit s'agir d'un pointeur [..] SO
[..] Les modifications apportées à Requête par le gestionnaire doivent être visibles sur le serveur. Nous ne le transmettons donc que par référence et non par valeur [..] SO
C'est faux ; en fait la documentation met explicitement en garde contre toute manipulation/mutation de la requête :
À l'exception de la lecture du corps, les gestionnaires ne doivent pas modifier la demande fournie.
Bien au contraire, non? :-)
Si vous souhaitez modifier la demande, par exemple ajoute un en-tête de traçage avant de le transmettre au gestionnaire suivant dans une chaîne de middleware vous devez copier la demande et transmettre la version copiée tout au long de la chaîne.
Pourquoi utiliser un pointeur si nous demandons explicitement aux personnes de ne pas modifier la demande? Performance , Request
est une grande structure et sa copie peut entraîner une baisse des performances, en particulier pour les longues chaînes de middleware. L’équipe a dû trouver un équilibre, ce n’est certainement pas une solution idéale, mais les compromis sont clairement du côté de la performance (au lieu de la sécurité des API).
Je pense que la raison principale pour laquelle l’objet Request
soit transmis en tant que pointeur est le champ Body
. Pour une requête HTTP donnée, le corps ne peut être lu qu'une seule fois. Si l'objet Request
était cloné, comme s'il n'était pas transmis en tant que pointeur, nous aurions deux objets avec des informations différentes sur le volume de lecture du corps.
La raison pour laquelle il s’agit d’un pointeur sur Request est simple: les modifications apportées à Request par le gestionnaire doivent être visibles par le serveur; nous ne le transmettons donc que par référence et non par valeur.
Si vous creusez dans le code de la bibliothèque net/http, vous constaterez que ResponseWriter est une interface vers une réponse struct non exportée, et nous transmettons la struct par référence (nous transmettons un pointeur sur la réponse) et non par valeur. . ResponseWriter est une interface utilisée par un gestionnaire pour créer une réponse HTTP. La structure qui sauvegarde ResponseWriter est la structure non exportée http.response. Comme il n’est pas exporté, vous ne pouvez pas l’utiliser directement; vous ne pouvez l'utiliser que via l'interface ResponseWriter.
En d'autres termes, les deux paramètres sont transmis par référence; c'est juste que la signature de la méthode prend un ResponseWriter qui est une interface vers un pointeur sur une structure, donc il semble que ce soit passé par valeur.