web-dev-qa-db-fra.com

"ne peut pas prendre l'adresse de" et "ne peut pas appeler la méthode du pointeur sur"

Cela compile et fonctionne:

diff := projected.Minus(c.Origin)
dir := diff.Normalize()

Cela ne signifie pas (donne les erreurs dans le titre):

dir := projected.Minus(c.Origin).Normalize()

Quelqu'un peut-il m'aider à comprendre pourquoi? (apprentissage Go)

Voici ces méthodes:

// Minus subtracts another vector from this one
func (a *Vector3) Minus(b Vector3) Vector3 {
    return Vector3{a.X - b.X, a.Y - b.Y, a.Z - b.Z}
}

// Normalize makes the vector of length 1
func (a *Vector3) Normalize() Vector3 {
    d := a.Length()
    return Vector3{a.X / d, a.Y / d, a.Z / d}
}
15
hunterloftis

La méthode Vector3.Normalize() a un récepteur pointer, donc pour appeler cette méthode, un pointeur vers la valeur Vector3 Est requis (*Vector3 ). Dans votre premier exemple, vous stockez la valeur de retour de Vector3.Minus() dans une variable, qui sera de type Vector3.

Les variables dans Go sont adressables et lorsque vous écrivez diff.Normalize(), il s'agit d'un raccourci et le compilateur prendra automatiquement l'adresse de la variable diff pour avoir la valeur de récepteur requise de type *Vector3 Pour appeler Normalize(). Ainsi, le compilateur le "transformera" en

(&diff).Normalize()

Ceci est détaillé dans Spec: Calls:

Un appel de méthode x.m() est valide si ensemble de méthodes de (le type de) x contient m et la liste d'arguments peut être affectée à la liste des paramètres de m. Si x est adressable et que l'ensemble de méthodes de &x Contient m, x.m() est un raccourci pour (&x).m().

La raison pour laquelle votre deuxième exemple ne fonctionne pas est que les valeurs de retour des appels de fonction et de méthode ne sont pas adressables , donc le compilateur n'est pas en mesure de faire le de même ici, le compilateur n'est pas en mesure de prendre l'adresse de la valeur de retour de l'appel Vector3.Minus().

Ce qui est adressable est exactement répertorié dans le Spec: Opérateurs d'adresse:

L'opérande doit être adressable, c'est-à-dire soit une opération d'indexation de variable, de pointeur ou de tranche; ou un sélecteur de champ d'un opérande struct adressable; ou une opération d'indexation de tableau d'un tableau adressable. Par exception à l'exigence d'adressabilité, x [dans l'expression de &x] Peut également être un (éventuellement entre parenthèses) littéral composite .

Voir les questions connexes:

Comment obtenir le pointeur de la valeur de retour de l'appel de fonction?

Comment puis-je stocker une référence au résultat d'une opération dans Go?

"Solutions de contournement" possibles

"Le plus simple" (nécessitant le moins de changement) est simplement d'affecter à une variable et d'appeler la méthode après cela. Ceci est votre première solution de travail.

Une autre façon consiste à modifier les méthodes pour avoir un récepteur de valeur (au lieu d'un récepteur de pointeur), afin qu'il ne soit pas nécessaire de prendre l'adresse des valeurs de retour des méthodes, afin que les appels puissent être "chaînés". Notez que cela pourrait ne pas être viable si une méthode doit modifier le récepteur, car cela n'est possible que s'il s'agit d'un pointeur (car le récepteur est transmis comme tous les autres paramètres - en faisant une copie -, et s'il ne s'agit pas d'un pointeur , vous ne pouviez que modifier la copie).

Une autre façon consiste à modifier les valeurs de retour pour renvoyer des pointeurs (*Vector3) Au lieu de Vector3. Si la valeur de retour est déjà un pointeur, pas besoin de prendre son adresse car elle est bonne pour le récepteur à une méthode qui nécessite un récepteur de pointeur.

Vous pouvez également créer une fonction d'assistance simple qui renvoie son adresse. Cela pourrait ressembler à ceci:

func pv(v Vector3) *Vector3 {
    return &v
}

En l'utilisant:

dir := pv(projected.Minus(c.Origin)).Normalize()

Cela pourrait également être une méthode de Vector3, Par exemple:

func (v Vector3) pv() *Vector3 {
    return &v
}

Et puis en l'utilisant:

dir := projected.Minus(c.Origin).pv().Normalize()

Quelques notes:

Si votre type ne comprend que 3 valeurs float64, Vous ne devriez pas voir de différences de performances significatives. Mais vous devez être cohérent sur votre récepteur et les types de résultats. Si la plupart de vos méthodes ont des récepteurs de pointeur, il en est de même pour toutes. Si la plupart de vos méthodes renvoient des pointeurs, il en est de même pour toutes.

26
icza

La réponse acceptée est vraiment longue donc je vais juste poster ce qui m'a aidé:

J'ai eu cette erreur concernant cette ligne:

services.HashingServices{}.Hash("blabla")

donc je l'ai juste changé en:

(&services.HashingServices{}).Hash("blabla")
0
VitalyGo877