---
id: 548504c5a0b4857763527603f3e1059e
author: snowmerak
title: Any n'est pas Any, mais Any est Any
description: Ce document analyse en détail le type `any` du langage Go ainsi que sa structure interne `EFace`, et présente les méthodes de traitement dynamique des types au moyen du package `reflect` ainsi que des assertions et des commutateurs de types.
language: fr
date: 2025-09-30T15:12:53.689280321Z
path: /blog/posts/any-is-not-any-but-any-is-any-z6161856d
category: language
---

## Any

En Go, le type any est un alias de `interface{}`, une interface magique capable de contenir n'importe quel type. Autrement dit, une variable de type any peut recevoir une valeur de n'importe quel type.

```go
var a any
a = 42          // int
a = "hello"     // string
a = true        // bool
a = 3.14        // float64
```

Ainsi, le type any peut contenir des valeurs de types variés, ce qui permet d'écrire du code souple. Toutefois, l'utilisation du type any exige certaines précautions.

### Type Assertion

Pour utiliser la valeur réelle contenue dans une variable de type any, il faut recourir à l'assertion de type (type assertion). L'assertion de type indique au compilateur que la variable de type any correspond à un type spécifique.

```go
var a any = "hello"
str, ok := a.(string)
if ok {
    fmt.Println("String value:", str)
} else {
    fmt.Println("Not a string")
}
```

Dans l'exemple ci-dessus, `a.(string)` affirme que `a` est de type string. Si `a` n'est pas de type string, `ok` vaut false, ce qui permet d'éviter que le programme ne entre en panique.

### Type Switch

Le sélecteur de type (type switch) permet d'exécuter différents comportements selon le type réel d'une variable de type any. Il est utile lorsqu'il faut traiter plusieurs types.

```go
var a any = 42
switch v := a.(type) {
case int:
    fmt.Println("Integer:", v)
case string:
    fmt.Println("String:", v)
case bool:
    fmt.Println("Boolean:", v)
default:
    fmt.Println("Unknown type")
}
```

Dans l'exemple ci-dessus, `a.(type)` examine le type réel de `a` et exécute le cas correspondant à ce type.

### Reflection

Le package reflect de Go permet d'examiner et de manipuler dynamiquement, à l'exécution, le type et la valeur d'une variable de type any. Cela s'avère utile lorsqu'on manipule des structures de données complexes.

```go
import (
    "fmt"
    "reflect"
)

var a any = 3.14
v := reflect.ValueOf(a)
fmt.Println("Type:", v.Type())
fmt.Println("Value:", v.Float())
```

Dans l'exemple ci-dessus, le package reflect permet de vérifier le type et la valeur de `a` à l'exécution.

## EFace

En Go, les interfaces se répartissent en deux formes principales : EFace (Empty Interface) et IFace (Interface with Methods). Parmi celles-ci, EFace correspond au concept identique à celui du type any que nous avons abordé jusqu'ici.

### Structure

EFace est une structure particulière qui n'existe qu'à l'intérieur du runtime de Go ; nous allons donc la manipuler à travers un mimétisme (mimic).

```go
type runtimeTypeMimic struct {
	Size_       uintptr
	PtrBytes    uintptr // number of (prefix) bytes in the type that can contain pointers
	Hash        uint32  // hash of type; avoids computation in hash tables
	TFlag       uint8   // extra type information flags
	Align_      uint8   // alignment of variable with this type
	FieldAlign_ uint8   // alignment of struct field with this type
	Kind_       uint8   // enumeration for C
	Equal       func(unsafe.Pointer, unsafe.Pointer) bool
	GCData      *byte
	Str         int32 // string form
	PtrToThis   int32 // type for pointer to this type, may be zero
}

type eFaceMimic struct {
	Type *runtimeTypeMimic
	Data unsafe.Pointer
}
```

EFace possède deux champs :

- `Type` : pointeur vers les métadonnées décrivant le type réel de la valeur.
- `Data` : pointeur vers la valeur réelle.

De plus, EFace gère les informations de type de manière interne au moyen de la structure `runtimeType`. Cette structure contient diverses métadonnées, telles que la taille, l'alignement et la valeur de hachage du type.

Les champs sur lesquels nous devons porter notre attention sont `Kind_`, `Hash` et `Equal`. `Kind_` indique la catégorie du type, tandis que `Hash` et `Equal` servent à comparer les types et à calculer leur valeur de hachage dans les tables de hachage.

### Encapsulation

Enveloppons maintenant cette EFace afin de pouvoir utiliser plus facilement cette magie noire.

```go
type TypeInfo struct {
	Hash  uint32
	TFlag uint8
	Kind  uint8
	Equal func(unsafe.Pointer, unsafe.Pointer) bool
	This  unsafe.Pointer
}

func GetTypeInfo(v any) TypeInfo {
	eface := *(*eFaceMimic)(unsafe.Pointer(&v))
	return TypeInfo{
		Hash:  eface.Type.Hash,
		TFlag: eface.Type.TFlag,
		Kind:  eface.Type.Kind_,
		Equal: eface.Type.Equal,
		This:  eface.Data,
	}
}
```

La fonction ci-dessus reçoit une variable de type any, extrait la structure EFace interne, puis en tire les informations de type qu'elle renvoie sous forme de structure `TypeInfo`. Il devient alors aisé d'accéder au hachage du type, à sa catégorie, à sa fonction de comparaison, etc. au moyen de ce `TypeInfo`.

### Utilisation

L'utilisation est très simple : il suffit de transmettre une variable de type any à la fonction `GetTypeInfo`.

```go
func main() {
	i := 42
	eface := GetTypeInfo(i)
	fmt.Printf("%+v\n", eface)

	f := 3.14
	eface2 := GetTypeInfo(f)
	fmt.Printf("%+v\n", eface2)

	// Compare the two values using the Equal function from the type info
	log.Println(eface.Equal(eface.This, eface.This))
	log.Println(eface2.Equal(eface2.This, eface2.This))
	log.Println(eface2.Equal(eface.This, eface2.This))
	log.Println(eface.Equal(eface2.This, eface.This))
}
```

Dans l'exemple ci-dessus, les valeurs entière et à virgule flottante sont transmises à la fonction `GetTypeInfo` afin d'afficher leurs informations de type respectives, puis la fonction `Equal` sert à comparer les valeurs. Grâce à cette approche, on peut obtenir plus rapidement les informations de type et comparer les types.

De plus, cette méthode s'applique de la même manière aux structures, aux pointeurs et aux interfaces, et permet aussi de résoudre le problème fréquent du typed nil. `Hash` reçoit une valeur dès lors que le type existe, alors que `This` vaut 0 lorsqu'il est nil.

## Conclusion

EFace joue aujourd'hui un rôle essentiel dans la manipulation du type any en Go. Grâce à elle, on peut traiter des valeurs de types variés de manière souple et rapide, et obtenir et exploiter en détail les informations de type. Toutefois, il s'agit d'une fonctionnalité cachée au sein du runtime, susceptible d'être affectée par les évolutions de la spécification du langage ; il convient donc de rester prudent.
