Generics in Go : transformer la réutilisabilité du code
Les génériques, introduits dans Go 1.18, ont révolutionné la manière d'écrire du code réutilisable et de type sécurisé. Les génériques apportent flexibilité et puissance tout en conservant la philosophie de simplicité de Go. Cependant, comprendre les nuances, les avantages et la façon dont les génériques se comparent aux approches traditionnelles (comme interface{} ) nécessite un examen plus approfondi.
Explorons les subtilités des génériques, approfondissons les contraintes, comparons les génériques à l'interface{} et démontrons leurs applications pratiques. Nous aborderons également les considérations de performances et les implications en matière de taille binaire. Allons-y !
Qu’est-ce que les génériques ?
Les génériques permettent aux développeurs d'écrire des fonctions et des structures de données pouvant fonctionner sur n'importe quel type tout en maintenant la sécurité des types. Au lieu de s'appuyer sur interface{}, qui implique des assertions de type au moment de l'exécution, les génériques vous permettent de spécifier un ensemble de contraintes qui dictent les opérations autorisées sur les types.
Syntaxe
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
T : Un paramètre de type, représentant un espace réservé pour le type.
TypeConstraint : restreint le type de T à un type spécifique ou à un ensemble de types.
parameterName T : Le paramètre utilise le type générique T.
ReturnType : La fonction peut également renvoyer une valeur de type T.
Exemple
func Sum[T int | float64](a, b T) T { return a + b }
func Sum : Déclare le nom de la fonction, Sum
[T int | float64] : Spécifie une liste de paramètres de type qui introduit T comme paramètre de type, limité à des types spécifiques (int ou float64). La fonction Somme ne peut prendre que des paramètres int ou float64, pas en combinaison, les deux doivent être soit int, soit float64. Nous explorerons cela plus en détail dans les sections ci-dessous.
(a, b T): Déclare deux paramètres, a et b, tous deux de type T (le type générique ).
T : Spécifie le type de retour de la fonction, qui correspond au paramètre de type T.
Contraintes : éléments constitutifs des génériques
Les contraintes définissent quelles opérations sont valides pour un type générique. Go fournit des outils puissants pour les contraintes, y compris le package de contraintes expérimentales (golang.org/x/exp/constraints).
Contraintes intégrées
Go a introduit des contraintes intégrées avec les génériques pour assurer la sécurité des types tout en permettant une flexibilité dans la définition de code réutilisable et générique. Ces contraintes permettent aux développeurs d'appliquer des règles sur les types utilisés dans les fonctions ou types génériques.
Go a des contraintes intégrées ci-dessous
- any : représente n'importe quel type. C'est un alias pour interface{}. Ceci est utilisé lorsqu'aucune contrainte n'est nécessaire
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
- comparable : autorise les types qui prennent en charge la comparaison d'égalité (== et !=). Utile pour les clés de cartes, la détection des doublons ou les contrôles d'égalité. Cela ne peut pas être utilisé pour les cartes, les tranches et les fonctions, car ces types ne prennent pas en charge la comparaison directe.
func Sum[T int | float64](a, b T) T { return a + b }
Contraintes expérimentales
- constraints.Complex : autorise les types numériques complexes (complex64 et complex128).
- constraints.Float : autorise les types numériques float (float32 et float64)
- constraints.Integer : autorise tout entier signé et non signé (int8, int16, int32, int64, int, uint8, uint16, uint32, uint64 et uint)
- constraints.Signed : autorise tout entier signé (int8, int16, int32, int64 et int)
- constraints.Unsigned : autorise tout entier non signé (uint8, uint16, uint32, uint64 et uint).
- constraint.Ordered : autorise les types qui permettent la comparaison (<. <=, >, >=), tous les types numériques et chaînes sont pris en charge (int, float64, string, etc.).
func PrintValues[T any](values []T) { for _, v := range values { fmt.Println(v) } }
Contraintes personnalisées
Les contraintes personnalisées sont des interfaces qui définissent un ensemble de types ou de comportements de type qu'un paramètre de type générique doit satisfaire. En créant vos propres contraintes, nous pouvons ;
Limiter les types à un sous-ensemble spécifique, tel que les types numériques.
Exiger des types pour implémenter des méthodes ou des comportements spécifiques.
Ajoutez plus de contrôle et de spécificité à vos fonctions et types génériques.
Syntaxe
func CheckDuplicates[T comparable](items []T) []T { seen := make(map[T]bool) duplicates := []T{} for _, item := range items { if seen[item] { duplicates = append(duplicates, item) } else { seen[item] = true } } return duplicates }
Exemple
import ( "golang.org/x/exp/constraints" "fmt" ) func SortSlice[T constraints.Ordered](items []T) []T { sorted := append([]T{}, items...) // Copy slice sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) return sorted } func main() { nums := []int{5, 2, 9, 1} fmt.Println(SortSlice(nums)) // Output: [1 2 5 9] words := []string{"banana", "apple", "cherry"} fmt.Println(SortSlice(words)) // Output: [apple banana cherry] }
La fonction Somme peut être appelée en utilisant uniquement les paramètres int, int64 et float64.
Contraintes par méthode
Si vous souhaitez imposer qu'un type doive implémenter certaines méthodes, vous pouvez le définir en utilisant ces méthodes.
type Numeric interface { int | float64 | uint }
La contrainte Formatter requiert que tout type utilisé comme T doit avoir une méthode Format qui renvoie un chaîne.
Combinaison de contraintes
Les contraintes personnalisées peuvent combiner des ensembles de types et des exigences de méthode
type Number interface { int | int64 | float64 } func Sum[T Number](a, b T) T { return a + b }
Cette contrainte inclut à la fois des types spécifiques (int, float54) et nécessite la présence d'une méthode abs.
Génériques vs interface{}
Avant l'introduction des génériques, l'interface{} était utilisée pour obtenir de la flexibilité. Cependant, cette approche a des limites.
Sécurité
interface{} : s'appuie sur des assertions de type d'exécution, augmentant le risque d'erreurs au moment de l'exécution.
Génériques : offre une sécurité de type au moment de la compilation, détectant les erreurs dès le début du développement.
Performance
interface{} : plus lente en raison de vérifications de type d'exécution supplémentaires.
Génériques : plus rapide, car le compilateur génère des chemins de code optimisés spécifiques aux types.
Lisibilité du code
interface{} : souvent verbeuse et moins intuitive, ce qui rend le code plus difficile à maintenir.
Génériques : une syntaxe plus propre conduit à un code plus intuitif et maintenable.
Taille binaire
interface{} : génère des binaires plus petits car elle ne duplique pas le code pour différents types.
Génériques : augmente légèrement la taille du binaire en raison de la spécialisation du type pour de meilleures performances.
Exemple
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
Le code fonctionne bien, l'assertion de type est une surcharge. La fonction Add peut être appelée avec n'importe quel argument, les paramètres a et b peuvent être de types différents, mais le code plantera lors de l'exécution.
func Sum[T int | float64](a, b T) T { return a + b }
Les génériques éliminent le risque de panique d'exécution causée par des assertions de type incorrectes et améliorent la clarté.
Performance
Les génériques produisent du code spécialisé pour chaque type, conduisant à de meilleures performances d'exécution par rapport à l'interface{}.
Taille binaire
Un compromis existe : les génériques augmentent la taille des binaires en raison de la duplication de code pour chaque type, mais cela est souvent négligeable par rapport aux avantages.
Limites des génériques Go
Complexité des contraintes : Même si les contraintes ressemblent à des contraintes.Ordonnées, elles simplifient les cas d'utilisation courants, la définition de contraintes hautement personnalisées peut devenir verbeuse.
Aucune inférence de type dans les structures : Contrairement aux fonctions, vous devez spécifier explicitement le paramètre de type pour les structures.
func PrintValues[T any](values []T) { for _, v := range values { fmt.Println(v) } }
Limité aux contraintes de temps de compilation : Les génériques Go se concentrent sur la sécurité au moment de la compilation, tandis que des langages comme Rust offrent des contraintes plus puissantes utilisant des durées de vie et des traits.
Faisons un benchmark – Mieux fait que dit
Nous allons implémenter une file d'attente simple avec à la fois une interface{} et générique et comparer les résultats.
Interface{} Implémentation de la file d'attente
func CheckDuplicates[T comparable](items []T) []T { seen := make(map[T]bool) duplicates := []T{} for _, item := range items { if seen[item] { duplicates = append(duplicates, item) } else { seen[item] = true } } return duplicates }
Implémentation de file d'attente générique
import ( "golang.org/x/exp/constraints" "fmt" ) func SortSlice[T constraints.Ordered](items []T) []T { sorted := append([]T{}, items...) // Copy slice sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) return sorted } func main() { nums := []int{5, 2, 9, 1} fmt.Println(SortSlice(nums)) // Output: [1 2 5 9] words := []string{"banana", "apple", "cherry"} fmt.Println(SortSlice(words)) // Output: [apple banana cherry] }
type Numeric interface { int | float64 | uint }
Analyse des résultats
Durée d'exécution :
L'implémentation générique est environ 63,64 % plus rapide que la version interface{} car elle évite les assertions de type d'exécution et fonctionne directement sur le type donné.Allocations :
La version interface{} effectue 3 fois plus d'allocations, principalement en raison du boxing/unboxing lors de l'insertion et de la récupération de valeurs. Cela ajoute des frais généraux à la collecte des ordures.
Pour les charges de travail plus importantes, telles qu'un million d'opérations de mise en file d'attente/retrait de la file d'attente, l'écart de performances se creuse. Les applications du monde réel ayant des exigences de débit élevé (par exemple, files d'attente de messages, planificateurs de tâches) bénéficient considérablement des génériques.
Pensées finales
Generics in Go établit un équilibre entre puissance et simplicité et offre une solution pratique pour écrire du code réutilisable et sécurisé. Bien qu'il ne soit pas aussi riche en fonctionnalités que Rust ou C, il s'aligne parfaitement sur la philosophie minimaliste de Go. Comprendre les contraintes telles que les contraintes. Commander et exploiter efficacement les génériques peut grandement améliorer la qualité et la maintenabilité du code.
À mesure que les génériques continuent d’évoluer, ils sont destinés à jouer un rôle central dans l’écosystème de Go. Alors plongez, expérimentez et adoptez la nouvelle ère de sécurité des types et de flexibilité dans la programmation Go !
Consultez le référentiel github pour quelques exemples sur les génériques.
sadananddodawadakar
/
AllerGénériques
Le référentiel contient des exemples fonctionnels de génériques go
Go Generics : référentiel d'exemples complet
Bienvenue dans le Référentiel Go Generics ! Ce référentiel est une ressource unique pour comprendre, apprendre et maîtriser les génériques dans Go, introduits dans la version 1.18. Les génériques apportent la puissance des paramètres de type à Go, permettant aux développeurs d'écrire du code réutilisable et sécurisé sans compromettre les performances ou la lisibilité.
Ce référentiel contient des exemples soigneusement sélectionnés qui couvrent un large éventail de sujets, de la syntaxe de base aux modèles avancés et aux cas d'utilisation pratiques. Que vous soyez un développeur Go débutant ou expérimenté, cette collection vous aidera à exploiter efficacement les génériques dans vos projets.
? Qu'y a-t-il à l'intérieur
? Programmes génériques de base
Ces exemples présentent les concepts fondamentaux des génériques, vous aidant à comprendre la syntaxe et les fonctionnalités principales :
- GenericMap : démontre une fonction de carte générique pour transformer des tranches de tout type.
- Swap : Un exemple simple mais puissant d'échange générique de deux valeurs.
- FilterSlice : montre comment filtrer…
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds











Golang est meilleur que Python en termes de performances et d'évolutivité. 1) Les caractéristiques de type compilation de Golang et le modèle de concurrence efficace le font bien fonctionner dans des scénarios de concurrence élevés. 2) Python, en tant que langue interprétée, s'exécute lentement, mais peut optimiser les performances via des outils tels que Cython.

Golang est meilleur que C en concurrence, tandis que C est meilleur que Golang en vitesse brute. 1) Golang obtient une concurrence efficace par le goroutine et le canal, ce qui convient à la gestion d'un grand nombre de tâches simultanées. 2) C Grâce à l'optimisation du compilateur et à la bibliothèque standard, il offre des performances élevées près du matériel, adaptées aux applications qui nécessitent une optimisation extrême.

GOISIDEALFORBEGINNERNERS et combinant pour pourcloudandNetWorkServicesDuetOtssimplicity, Efficiency, andCurrencyFeatures.1) InstallgofromTheofficialwebsiteandverifywith'goversion'..2)

Golang convient au développement rapide et aux scénarios simultanés, et C convient aux scénarios où des performances extrêmes et un contrôle de bas niveau sont nécessaires. 1) Golang améliore les performances grâce à des mécanismes de collecte et de concurrence des ordures, et convient au développement de services Web à haute concurrence. 2) C réalise les performances ultimes grâce à la gestion manuelle de la mémoire et à l'optimisation du compilateur, et convient au développement du système intégré.

GOIMIMPACTSDEVENCEMENTSPOSITIVEMENTS INSPECT, EFFICACTION ET APPLICATION.1) VITESSE: GOCOMPILESQUICKLYANDRUNSEFFIÉMENT, IDEALFORLARGEPROROSTS.2) Efficacité: ITSCOMPEHENSIVESTANDARDLIBRARYREDUCEEXTERNEDENDENCES, EnhancingDevelovefficiency.3) Simplicité: Simplicité: Implicité de la manière

C est plus adapté aux scénarios où le contrôle direct des ressources matérielles et une optimisation élevée de performances sont nécessaires, tandis que Golang est plus adapté aux scénarios où un développement rapide et un traitement de concurrence élevé sont nécessaires. 1.C's Avantage est dans ses caractéristiques matérielles proches et à des capacités d'optimisation élevées, qui conviennent aux besoins de haute performance tels que le développement de jeux. 2. L'avantage de Golang réside dans sa syntaxe concise et son soutien à la concurrence naturelle, qui convient au développement élevé de services de concurrence.

Golang et Python ont chacun leurs propres avantages: Golang convient aux performances élevées et à la programmation simultanée, tandis que Python convient à la science des données et au développement Web. Golang est connu pour son modèle de concurrence et ses performances efficaces, tandis que Python est connu pour sa syntaxe concise et son écosystème de bibliothèque riche.

Les différences de performance entre Golang et C se reflètent principalement dans la gestion de la mémoire, l'optimisation de la compilation et l'efficacité du temps d'exécution. 1) Le mécanisme de collecte des ordures de Golang est pratique mais peut affecter les performances, 2) la gestion manuelle de C et l'optimisation du compilateur sont plus efficaces dans l'informatique récursive.
