Structures & Pointeurs

Définition 
 Simplement Type personnalisé représentant une collection de champs 
 Syntaxe 
 type <NomStruct> struct {
	var1 int
	var2 string
	var3 float64
}
 
 Exemple 
 type User struct {
	Name string
	Email string
	Age int
}
 
 Déclaration 
 Il y a 3 types de déclaration possible 
 type Person struct {
	Name string
	Age int
}

func main() {
 // 1
	var p1 Person
	p1.Name = "Bob"
	p1.Addr.city = "Lyon"
	
	// 2
	p2 := Person{"Paul","Abibi"}

	// 3
	p3 := Person{Name: "Swann"}
}
 
 Règles 
 
 Une structure ne peut contenir que des variables 
 La règle de visibilité de package s'applique pour :
 
 la structure elle-même 
 les variables de la structure 
 
 
 
 Exercice player 
 
 Définir 2 structure :
 
 Avatar
 
 Url 
 
 
 Player
 
 Name 
 Age 
 Avatar 
 password 
 
 
 
 
 Le mot de passe doit avoir un scope privé. 
 Créer une fonction de création d’un player, qui ne prend que son nom en argument et initialise la structure avec ce dernier, ainsi qu’un mot de passe par défaut. 
 
 
 Solution 
 type Avatar struct {
	Url string
}

type Player struct {
	Name string
	Age int
	Avatar Avatar
	password string
}

func New(name string) Player {
	return Player{
		Name: name,
		password: "defaultpassword",
	}
}
 
 Embedded struct 
 type Avatar struct {
	Url string
}

type Player struct {
	Name string
	Avatar Avatar
}
 
 Un Player a un Avatar 
 
 Parfois, on veut exprimer un autre type de relation → Un XXX est un YYY 
 Dans d’autres langages, cette relation est exprimée par l’héritage 
 Go préfère la composition avec l’embedded struct : 
 type Avatar struct {
	Url string
}

type Player struct {
	Name string
	Avatar // Pas de nom de variables
}

var p Player
p.Url = "https://photodemoi.jpg"

 
 Dans ce code, Avatar est embarqué dans le type Player 
 Player est un Avatar 
 Receiver function 
 Grâce à ce fonctionnent, on peut enfin reproduire quasiment à l’identique le comportement d’un objet tel qu’en Java par exemple. 
 type User struct {
	Name string
}
func (u User) SayHello() {
	fmt.Printf("Hello %v!\n", u.Name)
}
 
 
 
 u est un value receiver pour la méthode SayHello() 
 
 
 Les champs de u sont accessibles pour la fonction 
 
 
 C’est bien beau ça, mais à quoi ça nous sert ? 
 func main() {
 u := User{"Paul"}
 u.SayHello()
} 
 
 
 Lorsqu’on utilise cette technique, notre struct User passé en argument est en réalité “copiée” pour la méthode. 
 Conséquence : Une méthode avec un value receiver ne peut pas modifier la structure originale. Cela peut permettre de favoriser l’immutabilité en renvoyant une nouvelle instance de notre structure avec les propriétés mise à jour ! 
 Exercice rectangle 
 
 Définir une structure rectangle qui contient
 
 Longueur 
 Largeur 
 
 
 Créer 3 receiver functions pour cette structure :
 
 Area() : renvoie l’air du rectangle 
 String() : Affiche les informations de la structure bien formatées 
 DoubleSize() : Renvoie une nouvelle structure du rectangle avec sa taille doublée 
 
 
 
 Astuce Définir la receiver function String() d’une structure viendra surcharger l’affichage par défaut de cette dernière ! 
 
 Solution 
 package main

import (
	"fmt"
)

type Rect struct {
	Width, Height int
}

func (r Rect) Area() int {
	return r.Width * r.Height
}

func (r Rect) String() string {
	return fmt.Sprintf("Rect ==> width=%v, height=%v", r.Width, r.Height)
}

func (r Rect) DoubleSize() Rect {
	r.Width *= 2
	r.Height *= 2
	return r
}

func main() {
	r := Rect{2, 4}
	fmt.Printf("Rect area=%v\n", r.Area())
	fmt.Println(r)

	r2 := r.DoubleSize()
	fmt.Println("r", r)
	fmt.Println("r2", r2)
} 
 Pointeurs 
 En Go, lorsque qu’on passe un paramètre à une fonction, on passe en réalité une copie de cette dernière, 
 Les pointeurs en Go fonctionnent presque comme en C, à l’exception que nous n’avons pas à gérer l’allocation et la libération de la mémoire. 
 x := -42
s := "Bob"
p := &x // Création d'un pointer vers la variable x
i := *p // Déréférencement de p pour récupérer la valeur de x
 
 Manipulations 
 func UpdateVal(val string) {
	val = "value"
}

func UpdatePtr(ptr *string) {
	*ptr = "pointer"
}

func main() {
	i := 1
	var p *int = &i

	fmt.Printf("i=%v\n", i)
	fmt.Printf("p=%v\n", p)
	fmt.Printf("*p=%v\n", *p)
	fmt.Println("---------------")

	s := "Paul"
	sPtr := &s
	s2 := *sPtr
	fmt.Println("String pointer")
	fmt.Printf("*s=%v\n", s)
	fmt.Printf("*sPtr=%v\n", *sPtr)
	fmt.Printf("s2=%v\n", s2)
	fmt.Println("---------------")

	*sPtr = "Clément"
	fmt.Println("Dereference and update")
	fmt.Printf("s=%v\n", s)
	fmt.Printf("*sPtr=%v\n", *sPtr)
	fmt.Printf("s2=%v\n", s2)
	fmt.Println("---------------")

	UpdateVal(s)
	fmt.Println("Func UpdateVal()")
	fmt.Printf("s=%v\n", s)
	fmt.Printf("*sPtr=%v\n", *sPtr)
	fmt.Println("---------------")

	UpdatePtr(&s)
	UpdatePtr(sPtr)
	fmt.Println("Func UpdatePtr()")
	fmt.Printf("s=%v\n", s)
	fmt.Printf("*sPtr=%v\n", *sPtr)
	fmt.Println("---------------")
}
 
 Pointer Receiver 
 Comme dit plus tôt, les paramètres de fonctions sont des copies des objets originaux. 
 Ça vaut aussi pour les fonctions value receiver sur les structures. Elles ne peuvent donc que faire de la lecture simple, et ne peuvent pas modifier la structure d’origine. 
 Grâce aux pointeurs, on peut régler ce problème et le couplant à des receiver functions . 
 type Post struct {
	Title string
	Text string
	published bool
}

func (p Post) Headline() string {
	return fmt.Sprintf("%v - %v", p.Title, p.Text[:50])
}

func (p *Post) Publish() {
	p.published = true
}

func (p *Post) Unpublish() {
	p.published = true
}

func main() {
	p := Post{
		Title: "Go release",
		Text: "Go is a programming language...",
	}

	fmt.Println(p.Headline())

	fmt.Printf("Post published? %v\n", p.Published())
	p.Publish()
	fmt.Printf("Post published? %v\n", p.Published())
}
 
 Enfin, si on souhaite créer une structure directement sous la forme d’un pointeur, on peut faire autrement que : 
 p := Post{
		Title: "Go release",
		Text: "Go is a programming language...",
	}
pointer := &p
 
 Comme ceci : 
 pythonPost := &Post{
		Title: "Python Intro",
		Text: "Python is an interpreted high-level programming language",
	}
 
 pythonPost est un pointeur.