Sommaire
- Mise en situation :
- Le programme :
- Conclusion
NdM : Ce journal a été promu en dépêche
Bonjour au journal et à la famille.
Je viens de finir un petit projet en Go la semaine dernière. Voici un petit retour d'expérience en espérant que ça serve !
Mise en situation :
Pour mes études j'ai un projet (le dernier avant la vie active !) de réalisation de microprocesseur MIPS "from scratch".
Le CPU est designé sous Xilinx ISE (grosso modo un IDE dans lequel on peut réaliser des designs de composants à base de portes logiques). Par la suite il sera chargé sur un fpga, celui-ci connecté à un robot afin de le faire suivre une ligne sur le sol.
De fait une fois le CPU designé, il faut réaliser un programme en assembleur MIPS, puis le convertir en binaire afin de l'intégrer dans le design du CPU sous la forme d'un module en vhdl. (en gros en entrée du module arrive l'adresse du program counter et le module sort l'instruction correspondante)
Les plus attentifs auront déjà pointé du doigt le soucis : comment convertir proprement le code assembleur MIPS en binaire ?
À la main
Ne riez pas, c'est ce que m'a proposé mon prof quand je lui ai posé la question ! (à sa décharge, les élèves suivant ce cours ne sont pas informaticiens mais plutôt orientés électronique)Utiliser un assembleur déjà existant
La solution "ne pas réinventer la roue" de référence. Le problème : l'output sera en binaire (logique me direz-vous) mais je veux du code binaire lisible ! (en gros mon output doit être 000101011100110 afin de pouvoir directement copier coller le code dans le fichier de mon module vhddl)
Ajouter à cela que je ne veux pas de header ELF ou quoi que ce soit, juste la transcription du code que j'ai écrit.
Je pense bien sûr qu'il y a des solutions pour arriver à ce que je veux. Néanmoins j'ai du temps en ce moment et apprendre un nouveau langage me semble plus formateur qu'apprendre les options d'un outil qui ne me servira plus par la suite !Écrire un assembleur à la main
La solution que j'ai choisi (et qui me donne donc la possibilité de réaliser ce journal !). Un assembleur n'est pas aussi complexe qu'un compilateur (et de loin !) mais permet déjà de s'amuser sur un nouveau langage.
Le programme :
Linus Torvald :
Show me the code !
Céans mon bon monsieur !. En espérant que tout le monde aime sourceforge…
Comme dit plus haut, il s'agit d'un assembleur MIPS "simplifié" :
Toute les instructions ne sont pas disponibles (pas de jump ou de subi par exemple). La raison est tout simplement que mon processeur ne supporte pas ces instructions et je préfère avoir une erreur à la compilation si j'oublie ce détail que de devoir débugger une erreur qui n'en est pas une par la suite… Malgré tout il est très simple d'ajouter ces fonctionnalités comme nous allons le voir.
Pas de header ELF pour le programme. Comme j'ai dit, l'idée et de copier la sortie de l'assembleur dans un fichier pour que mon cpu l'utilise "tel quel". Pas d'OS, pas le logique supérieur, rien ! Donc pas besoin d'header.
Rentrons dans le code
J'ai divisé mon assembleur en 3 parties :
- Le parser/lexer : Réaliser avec yacc pour le premier. On vérifie la grammaire et la sémantique afin de générer une liste d'instructions sous la forme d'un tableau de structures ainsi qu'une map faisant la conversion nom des label => adresse.
- Le binder : transforme chaque instruction du tableau précédent en une instruction binaire à proprement parlé (avec vérification en fonction du contexte)
- Le main : qui wrap ces deux parties, les connecte entre elles et gère les options fournies par la ligne de commande.
Et Go dans tout ça ?
Il convient avant tout de parler un peu de la philosophie de Go avant d'aller plus loin.
En effet l'idée est de fournir des outils tout prêt à l'utilisateur. On se base sur un maximum de règles standards et donc un minimum de configuration (voir en fait pas de configuration du tout dans bien des cas !).
Le makefile
L'exemple le plus frappant de ce concept est le makefile. Si dans les versions antérieurs à la 1.0, Go possédé un simili makefile (il était déjà beaucoup plus simple qu'un makefile typique pour du C), tout cela est révolu !
Maintenant un projet Go n'a besoin pour compiler que de ses sources. Un coup de "go build" et tout se fait tout seul. Plus de gestions des dépendances, plus de problèmes de conflits d'includes (de toute façon, il n'y a pas d'include en Go)… voila qui devrait intéresser, je pense, tout ceux qui se sont essayé à C++ et a ses célèbres erreurs de compilation hyper verbeuses pour cause de conflit de define pour avoir placé un include au mauvais endroit.
Petite remarque tout de même : Mon projet contient un makefile !
Bien que minimaliste, celui-ci est donc toujours présent.
La raison est multiple :
La commande "go build" construit votre binaire… et c'est tout ! J'aime pouvoir automatiser la génération de tarball, le nettoyage do projet etc…
Mon projet utilise yacc (donc conversion du fichier parser.y en parser.go). De fait, go build ne met pas à jours parser.go si parser.y est mis à jour. d'où la nécessité de gérer cette dépendance
Les tests
De la même manière, réaliser des tests est simplissime. Pour écrire des tests pour le fichier foo.go vous n'avez qu'à créer le fichier foo_test.go et… c'est tout ! Ce fichier se fera automatiquement compiler ses fonctions commençant par Test seront exécuté à chaque lancement de "go test".
Dans le fichier, on import le package "testing", et on appel la méthode testing.T.Fail() ou testing.T.Error("C'est la dèche !") pour signaler que le test a échoué :
import "testing"
func TestFoo(t *testing.T) { // Les fonctions de test commencent par Test
// et respectent cette signature
if test_is_ok() != nil {
t.Error("t'es parti pour fixer ton code !") // Erreur avec message
}
if test_sans_message != nil {
t.Fail() // Erreur sans message
}
// Si aucun appel à Fail ou Error, alors le test est considéré comme réussit
}
Le multiplatforme !
Encore une très bonne nouvelle : Go est multiplatforme de base !
Voulant partager mon logiciel avec mes petits camarades (Je précise que je suis en Corée du Sud actuellement… pas la peine de dire quel OS utilise tout ce joli monde !), autant dire que cette fonctionnalité a fortement pesé dans la balance pour le choix de Go.
De plus, selon la doc il est possible de cross-compiler à partir de n'importe quelle platforme pour n'importe quelle autre juste en changeant ses variables locales comme GOOS ou GOARCH (et bien sûr en compilant sa chaîne de compilation pour l'architecture cible). Toutefois je n'ai pas testé cette possibilité, j'ai préféré rebooter sous windows (à ma décharge, j'aurai de toute façon dû le faire pour vérifier que mon binaire marche bien !)
J'ai toutefois trouvé un peu bizarre que la gestion du retour à la ligne ne soit pas fournie comme en C++ (avec std::endl). De fait on doit faire attention à ce léger détail et différencier les cas selon les OS à la main ce qui est assez dommage.
Les outils en plus
En bonus, go fourni des outils des plus sympa :
go fmt
Cette commande permet de mettre à LA norme le code. Notez le "LA" majuscule, il n'y en a qu'une (certains diront qu'elle est horrible mais nous ne sommes pas vendredi, je laisse cela à d'autres). Du coup, pas de conflits à ce niveau, tous les codes go écris pas tous les développeurs du monde auront la même forme.
Petit bémol pour ma part : cette norme utilise des indentations de 8 caractères et ne coupe pas le code à 80 colonnes. Résultat celui-ci est bien souvent trop long à mon goût (ainsi qu'à celui de mon Emacs en multicolonnes. Là où je peux mettre 3 colonnes en C la plupart du temps, je suis limité à 2 en go… 33% d'espace perdu, snif !)go tool yacc
La commande go tool permet d'accéder à la foultitude d'outils intégré dans la commande go. Parmi eux se trouve yacc, le célèbre parser. Cette version est une réécriture en go de celui de plan9. Autant dire que sa présence à été déterminante dans mon choix d'utiliser go pour mon projet (un parser à la main… non merci !)
Écrivons un peu de code
Après tout ce temps à parler des outils, parlons du code, du vrai !
Le retour de plusieurs variables
En voila une bonne idée ! Toute fonction peut renvoyer plusieurs variables au lieu d'une seule dans la plupart des langages.
De fait on retrouve dans la lib standard de Go un bon nombre de fonctions renvoyant à la fois la valeur qu'on leur demande ainsi qu'un type "*Error" pouvant être soit "nil" (c'est à dire pointer sur rien) soit initialiser, signifiant alors une erreur. Plus besoins de "tricher" comme en C en donnant en argument de la fonction un pointer sur la variable à compléter puisque la variable de retour est déjà occupé par le code d'erreur.
De même le parcours de tableau s'en trouve simplifié :
for i,elm := range array {
fmt.Println("l'élément", i, "a pour valeur", elm)
}
range est un mot clé permettant de parcourir un tableau en renvoyant à chaque itération la position ainsi que l'élément courant.
Notez aussi le ":=" permettant de déclarer "à l'arrache" des variable en fonction du type de la variable qu'on lui assigne. Un vrai bonheur pour gagner en lisibilité dans les cas triviaux.
La gestion des options
Encore une bonne nouvelle ! La gestions des passages d'options étant une fonctionnalité essentiel à 90% des logiciels, le package "flag" s'occupe de cela avec brio !
import "flag"
var f_input = flag.String("i", "", "Input file.")
var f_output = flag.String("o", "", "Output file. (stdout if nothing specified)")
var f_type = flag.String("t", "vhdl", "Type of output : binary, print, vhdl")
func main() {
flag.Parse()
fmt.Println("lecture du fichier", *f_input)
...
}
Ai-je vraiment besoin d'expliciter ? On déclare les flags en dehors des fonctions, on appel flag.Parse() avant d'utiliser les flags (notez qu'il s'agit de pointers, on met donc une "*" pour les déférencer)
Remarquez que l'option --help/-h est gérée automatiquement !
Les conteneurs
Go possède de manière intégré au langage les conteneurs les plus utilisés :
Map
Son nom est suffisamment explicite : une clé, une valeur.
Problème selon moi : si la clé ne correspond à aucune valeur, la valeur nulle est renvoyée. De fait dans mon programme j'utilise une map pour faire la correspondance entre les labels et leur adresse réelle. Si je demande l'adresse d'un label inexistant, la map va me renvoyer la valeur 0 puisque c'est l'équivalent de la valeur nulle pour un int…
Problème : cette valeur peu tout à fait être valide dans le cas d'un label situé en début de code ! C'est d'autant plus étrange que le langage autorise le renvoie de plusieurs valeurs comme nous l'avons vu précédemment…
Array
Un jolie tableau unidimensionnel de taille constante… rien à redire.
Slice
Un type four tout : c'est un genre de tableau à taille variable.
En réalité le slice, comme son nom l'indique, représente un morceau de Array. De fait plusieurs slices peuvent pointer en même temps sur le même Array par exemple.
Je cherchais à utiliser un vecteur pour stocker chacun de mes instructions une fois parsées/lexées. J'ai eu la surprise de voir que le conteneur Vecteur avait été supprimé de la lib standard il y a quelques commits…
La réponse s'est trouvé sur la mailling list de Go : utiliser des slices !
Voici comment faire :
var s0 := []int{0, 1} // on créé un un slice sur un tableau contenant 0 et 1
var to_insert = 42
S0 = append(s, to_insert) // on utilise la fonction buildtin "append"
Simple ? bon maintenant voyons comment supprimer l'élément numéro i (en C++ j'aurais fait "vect.remove(i)")
s0 = append(s[:i - 1], s[i + 1:]...)
Pas glop ! Je pense qu'un peu de sucre syntaxique n'aurais pas été de trop pour cacher cette complexité inutile !
Pour ceux qui se posent la question, append ajoute des éléments à un slice. De fait on doit transformer le slice s[i + 1:] en une suit d'élément avant de l'ajouter à s[:i - 1]. C'est ce qu'on fait avec la commande "s[i + 1:]…"
L'héritage
Dans Go l'héritage dans la grande tradition OO n'existe pas ! (et ce n'est pas pour me déplaire à titre personnel)
De tout s'articule autour de trois concepts (que je n'illustrerais pas avec mon programme puisse que celui-ci n'en n'utilise pas le premier.)
les structures
Plutôt que de déclarer des classes, on créé des structures. Celles-ci pouvant gérer l'héritage d'une façon amusante :
type plat struct {
name string
}
type choucroute struct {
_ plat
saucisses int
}
func main() {
var ch choucroute = choucroute{ "choucroute", 4 }
fmt.Println("Je vais me tapper une", ch.name, "avec ", ch.saucisses, "saucisses dedans !")
}
On remarque donc que en ajoutant dans choucroute une structure plat de manière anonyme (le nom "_" l'équivalent en Go de John Doe…) les éléments de plat sont intégrés dans choucroute !
Les méthodes
Une fois notre jolie structure déclarée, il est possible de lui adjoindre des méthodes :
func (ch Choucroute)manger() {
ch.saucisses--
fmt.Println("Miam ! Encore", ch.saucisses, "saucisses !")
}
func (_ Plat)manger() { // remarquez le "_" pour indiquer qu'on ne fera rien avec l'objet et qu'il n'est donc pas nécessaire de le nommer.
fmt.Println("Beark !")
}
Les interfaces
En Go, tout se base sur du Duck typing. De fait afin de pouvoir manger à la fois un plat de seconde zone où de la délicieuse choucroute, on peut déclarer une interface qui contiendra la fonction manger :
type mangerer interface {
manger()
}
func main() {
bouffe := []mangerer
bouffe = append(bouffe, choucroute{ "choucroute", 4 })
bouffe = append(bouffe, plat{ "bouts de tétons de mme Félipé" })
for _, pl := range bouffe {
pl.manger()
}
}
De fait le duck typing faisant son travail, notre slice peut contenir n'importe quel structure possédant une fonction ayant pour signature "manger()".
Au final un gain de légèreté monstrueux comparé au C++ et à ses déclarations d'objets et d'héritage particulièrement verbeuses.
C'est beau, c'est simple, ça me fait pleurer !
Les autres fonctionnalités
les goroutines
Bien sûr, je n'ai pas pu tout tester dans mon programme.
Je pense notamment à une des principales fonctionnalités mise en avant : la concurrence.
Mon programme n'utilise qu'un fil d'exécution. De fait, je n'ai pas pu utiliser les channels ni le mot clé go.
Toutefois, ayant déjà fait un peu joujou avec par le passé, j'ai trouvé l'idée vraiment exellente. Les channels permettant à une routine d'attendre qu'une autre lui envoie un objet pour continuer. De fait on résout le soucis de synchronisation de manière beaucoup plus simple qu'en plaçant des mutex sur les ressources critiques !
Conclusion
Je suis globalement très satisfait de Go, j'en ai l'impression d'un langage rapide, et ce dans tous les sens du terme :
Rapidité de compilation.
Quasi instantanée ! (dois-je comparer à un poid lourd comme C++ ?)Rapidité de développement.
Tout va très vite. Le duck typing permet de redéfinir ses structures très rapidement, on n'écrit que le minimum.
De même, Tout le monde est convaincu de l'importance des tests mais la flemme nous fait généralement (en particulier pour les petits projet comme le mien) tester à la main la fonctionnalité sur laquelle on travail actuellement et basta !
De Fait, le système de test intégré à Go est pour moi un pur bonheur tant il est simple est efficace !Rapidité d'exécution.
Le débat est lancé ! Pour certain le compilateur de Go est trop jeune et pas suffisamment optimisé (ce qui explique sa vitesse de compilation).
Les auteurs de Go ont notamment publié un article pour battre en brêche cette idée en montrant comment obtenir des performances proches du C++ en optimisant son code.
D'un autre côté, il s'agit d'un langage jeune, avec tous les problèmes inhérents :
Peu de libs tiers pour le moment
Au vu de la simplicité d'interfaçage de Go avec C, des projets de bindings de grosses libs fleurissent un peu partout mais pour le moment rien de très stable/utilisableUn compilateur jeune.
En effet, pour le moment la runtime est compilée statiquement dans le logiciel. Cela explique la taille supérieur au mégaoctet du moindre "hello world". Par exemple mon logiciel faisant dans les 800 lignes de Go se retrouve compilé dans un binaire de 1.6mo…
Enfin bon vu la taille actuelle de nos disques durs et tant que Go n'aurra pas pour vocation de tourner sur de l'embarqué, je ne suis pas sûr que ce soit un si gros point noir.
# Et les lib ?
Posté par purplepsycho . Évalué à 2.
Très bon article, merci.
Une question tout de même :
Et dans l'autre sens ? Est-il facile d'écrire une bibliothèque facile à piloter en C, l'ABI semble-t-elle définitive ?
# erratum
Posté par G.bleu (site web personnel) . Évalué à 8.
Je viens de me rendre compte que la fatique aidant, j'ai écrit n'importe quoi dans la partie sur l'héritage (je sais, j'aurai dû tester mon code avant !), si un modo pouvait faire les correction…
Pas de "_" avant le "plat" dans la déclaration de la structure
Par contre la création d'un type choucroute doit se faire comme ça :
Ce n'est que par la suite qu'on peut oublier le type plat :
Enfin la déclaration d'une interface se fait comme cela :
Voila, pardon aux familles tout ça…
[^] # Re: erratum
Posté par Ummon . Évalué à 1.
Un jour viendra où nous pourrons éditer les journaux sous LinuxFR voir même les commentaires. Ça prendra peut-être 10 ans de développement acharnés mais ils y arriveront!
# Abuser du sucre c'est mauvais pour la santé
Posté par reno . Évalué à 5.
vect.remove et l'opération que tu cites ne sont pas 100% identiques, dans un cas tu modifie le contenu d'un tableau existant dans l'autre tu alloue un nouveau tableau (à la mode fonctionnelle).
J'imagine qu'ils n'ont pas ajouté ces opérations car elles sont couteuses..
# Über pertinent
Posté par Enzo Bricolo 🛠⚙🛠 . Évalué à 4.
J'en parlais ce matin avec le concierge de la tribune. Le Duck typing est particulièrement adapté au développement de gocoincoin.
[^] # Re: Über pertinent
Posté par ナイコ (site web personnel) . Évalué à 2. Dernière modification le 16 mai 2012 à 16:08.
Bonne remarque : à quand les duckfaces dans gocoincoin ?
[^] # Re: Über pertinent
Posté par Enzo Bricolo 🛠⚙🛠 . Évalué à 2.
Et les totoz en 3d aussi ?
# Maps et slices
Posté par Yakulu . Évalué à 5.
Comme tu le dis, Go est capable de renvoyer plusieurs valeurs en retour de fonction.
Pour savoir si la clé existe, il faut utiliser le paradigme, fréquent en Go, dit comma ok :
Pour les slices, il manque peut-être un peu de sucre syntaxique oui, mais il est simple de créer une fonction remove(i) qui agira comme souhaité.
En tout cas sympathique retour et projet, je vais moi aussi utiliser Go pour mes développements à venir.
[^] # Re: Maps et slices
Posté par Nicolas Boulay (site web personnel) . Évalué à 4.
C'est très très moche quand même. C'est impossible de lire ce truc sans connaitre la priorité des 3 opérateurs.
"La première sécurité est la liberté"
[^] # Re: Maps et slices
Posté par Yakulu . Évalué à 2.
Je partage le même point de vue. J'aurais tendance à préférer la forme suivante, bien que demandant une ligne supplémentaire :
Quant au fait de pouvoir lire ce bout code sans connaître Go, il est surtout lié au nom donné à la variable de vérification de présence (et à la connaissance du langage, mais c'est vrai pour tous).
[^] # Re: Maps et slices
Posté par Moonz . Évalué à 2.
C’est un idiome assez courant dans ce langage, tu t’y fais très rapidement.
[^] # Re: Maps et slices
Posté par Nicolas Boulay (site web personnel) . Évalué à 2.
Cela viole plein de règle habituel sur la force de "," et la présence de ";" à la ocaml plutôt que la version du C.
"La première sécurité est la liberté"
[^] # Re: Maps et slices
Posté par Moonz . Évalué à 3. Dernière modification le 16 mai 2012 à 14:55.
Heu, en C "," est prioritaire devant ";"… exactement comme en Go.
Quant à OCaml je connais pas donc je ne saurai dire, mais la compatibilité de la syntaxe avec Caml n’est vraiment pas, mais alors pas du tout un objectif de Go.
Edit: niveau priorité des opérateurs, le ";" dans le "if" est géré en Go très exactement comme le ";" dans le for en Go, qui est lui-même géré exactement comme le ";" dans le for en C. Je vois pas ce qu’il y a de choquant niveau lisibilité ou priorités habituelles.
Pour expliciter,
if a;b { … }
est équivalent àfor a;b; { …; break; }
, qui existe en C. Rien de vraiment révolutionnaire ou traumatisant donc.[^] # Re: Maps et slices
Posté par Nicolas Boulay (site web personnel) . Évalué à 2.
Le for en C est quand même le cas particuliers d'utilisation du ";". Celui-ci sépare 2 instructions et ne peut pas être mis dans une expression.
"La première sécurité est la liberté"
[^] # Re: Maps et slices
Posté par Moonz . Évalué à 3.
Et c’est pareil en Go :
http://golang.org/ref/spec#If_statements
[^] # Re: Maps et slices
Posté par G.bleu (site web personnel) . Évalué à 2.
Je viens de corrigé mon code en conséquence, merci !
Je me sens un peu con pour le coup, j'ai mal compris la doc à propos des map, je pensais que cette fonctionnalité n'était pas supportée…
# s/désigner/designer
Posté par psychoslave__ (site web personnel) . Évalué à 10.
Designer et désigner sont deux mots distincts avec des sens complètement différent. Tu peux avantageusement remplacer designer par concevoir, pour lever toute ambiguïté, àmha.
Sur ce je m’en vais lire le reste du journal qui s’annonce intéressant, merci.
# Annyeon, et Map
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 3.
Tes petits camarades sont donc de Corée du nord, et utilisent donc Kim Jong Ilnux, l'OS codé par l'ancien Grand Leader du pays.
Tu pourrais aussi stocker l'adresse d'un label, plus 1. Comme ça, 1 correspond à l'adresse 0, 5 à l'adresse 4, et 0 à file not found.
# Abanon
Posté par Elfir3 . Évalué à 2.
Quelqu'un qui maîtrise un peu plus le c++ que moi pour me rectifier en cas d'erreur, mais il me semble que std::endl n'est ni plus ni moins qu'un alias vers "\n". C'est encore dans une couche sous-jacente (le compilateur?) qui fait la conversion vers le système cible.
[^] # Re: Abanon
Posté par claudex . Évalué à 4.
Si je ne me trompe pas, std:endl permet de s'affranchir du système de retour à la ligne du système. Par exemple, il peut retourner "\n" ou "\r\n" ou encore autre chose sur un autre système.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Abanon
Posté par serianox . Évalué à 5.
std::endl flush le buffer au passage. À utiliser avaec parcimonie car cela peut avoir un réel impact sur les performances.
[^] # Re: Abanon
Posté par zzmaxfr . Évalué à 3.
Pas tout a fait, le std::endl est à la fois
- un retour à la ligne.
- un flush (nettoyage) de la sortie utilisée. Soit vider les éventuels buffers internes pour afficher immédiatement (Sur un terminal par exemple).
[^] # Re: Abanon
Posté par Ummon . Évalué à 1.
Oui, tout à fait, d'ailleurs std::endl est une fonction dont la signature est "ostream& endl(ostream& os);".
# Ne pas réinventer la roue
Posté par serianox . Évalué à 7.
Mais c'est une bonne excuse pour écrire un assembleur ! ;)
[^] # Re: Ne pas réinventer la roue
Posté par superna (site web personnel) . Évalué à 4.
Tu as même la merveilleuse commande data2mem fournie avec ISE qui sait même convertir des ELF au bon format mémoire qui va bien pour les mémoires de FPGA…
RTFM !
Mais j'avoue, bonne excuse pour faire un soft en go !
Perso, on a utilisé objcopy pour sortir du SREC et un code en C pour réorganiser les données selon le type de mémoire voulu.
# Petites questions
Posté par claudex . Évalué à 5.
Est-il possible de ne récupérer qu'une partie des variables retournées (par exemple, uniquement celle qui l'erreur) ?
Existe-t'il un IDE pour Go (ou un projet d'IDE) ?
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Petites questions
Posté par Yakulu . Évalué à 2.
Oui, en utilisant la variable spéciale _ . Par exemple, lorsque tu itères un dictionnaire (map) et que tu ne veux utiliser que les valeurs :
Oui, il y en a des spécialisés, comme GolangIDE. Il y a également une extension Eclipse, IntelliJ… et des plugins Vim / Emacs (gocode pour l'autocomplétion)… La liste des efforts est concentrée sur Cat-V.
# Ca mérite une dépêche
Posté par palm123 (site web personnel) . Évalué à 5.
On parle beaucoup de GO, pour une fois que l'on voit du concret.
ウィズコロナ
[^] # Re: Ca mérite une dépêche
Posté par claudex . Évalué à 3.
C'est en cours, il manque un vote.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Ca mérite une dépêche
Posté par serianox . Évalué à 3.
À quand une fonctionnalité pour passer le journal en dépêche sans perdre le tout plein de comz ? :)
[^] # Re: Ca mérite une dépêche
Posté par claudex . Évalué à 2.
Je ne retrouve plus l'entrée de suivi mais on en a déjà discuter longuement, l'ambiance des commentaires des journaux ne sont pas les même que ceux des dépêches, sans compter qu'un journal est parfois modifié quand il passe en dépêche, ce qui rend beaucoup de commentaires obsolètes. De plus, les commentaires ne sont pas perdu, ils sont toujours accessibles dans le journal.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Ca mérite une dépêche
Posté par Octabrain . Évalué à 7.
Elle doit vraiment être dure à implémenter cette fonction pour que les devs en soient réduits à inventer des contre-raisons aussi minables.
[^] # Re: Ca mérite une dépêche
Posté par serianox . Évalué à 1.
C'est exact. Au temps pour moi. ;)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.