Rust: mot-clé var pour les sections locales modifiables uniquement

Créé le 20 juin 2012  ·  35Commentaires  ·  Source: rust-lang/rust

Le numéro 1273 proposait let mut pour les variables locales mutables et mut pour les champs mutables. let mut est plus détaillé qu'un seul mot-clé et rompt également l'alignement des colonnes. Les gens n'aimaient pas à juste titre l'idée de var pour les déclarations de champs mutables. Mais je pense que personne n'a suggéré l'idée d'utiliser var pour les déclarations locales mutables et mut pour les déclarations de champs mutables :

let x = 7; // declaration of immutable variable x
var y = 7; // declaration of mutable variable y
type point = { mut x: int, mut y: int }; // declaration of mutable record type

Dave

I-nominated

Tous les 35 commentaires

Je dois avouer que j'ai d'abord pensé que nous devrions utiliser le même mot-clé pour les variables mutables et les champs mutables, mais var lit plutôt bien (même si je n'aime pas let mut )

Je pense que c'est une caractéristique que la déclaration d'une variable mutable est un peu plus lourde qu'une variable immuable. Je peux voir des programmeurs utiliser simplement "var" pour être "cohérent" ou parce que "var" serait plus puissant (vous n'avez pas à le changer si vous décidez de muter plus tard). Ensuite, vous vous retrouvez avec un code globalement moins lisible.

Je ne suggère pas que Rust devrait être un langage sérieux de bondage et de discipline, mais un petit coup de coude dans la bonne direction est moralement justifiable, je pense (c'est-à-dire que la règle serait "pour un code sûr, les constructions les plus sûres devraient être moins bruyantes que les plus puissantes mais constructions plus faciles à gâcher").

D'un côté j'aime ça, car cela enlèverait l'ambiguïté visuelle de cette forme :

let mut x = 4,
    y = 8;  // is y mutable or not?

Mais je suis enclin à me ranger du côté de Ssylvan. Déclarer des mutables n'a pas besoin d'être exactement _moches_, mais je pense qu'il est logique qu'ils soient un peu moins pratiques à créer que des immuables, ne serait-ce que par une seule frappe. Le mot-clé d'activation doit également être distinctif, et (IMO) var est trop largement utilisé comme mot-clé générique de déclaration de variable pour transmettre spécifiquement la mutabilité dans un langage qui se targue de l'immuabilité par défaut. Et pensez aux pauvres programmeurs C#, pour qui var est notre let !

J'aime toujours l'idée de remplacer let mut par un seul mot-clé, afin de traiter l'extrait de code ci-dessus, mais y a-t-il une raison d'introduire un nouveau mot-clé lorsque mut lui-même suffirait, selon la proposition originale en #1273 ?

@pcwalton a signalé un problème mut : il existe une ambiguïté avec les littéraux d'enregistrement et les expressions de bloc qui nécessitent une anticipation arbitraire pour être résolue.

{ mut x: int ...

Littéral d'enregistrement ou bloc avec variable locale ?

J'ai pu voir beaucoup de programmeurs apprendre que var est la façon dont vous déclarez les variables dans Rust, et n'utilisez pas du tout let . Après tout, var est la façon dont vous déclarez les variables locales dans des langages comme JavaScript. Je suis enclin à penser que c'est une bonne chose à propos de cette proposition.

J'ai entendu quelqu'un souligner hier que maintenant let et var auraient la même longueur, ce qui serait bien pour l'alignement.

Je suis un peu indifférent, mais je préfère un peu var , car c'est plus court. Rendre la mutabilité ennuyeuse n'est pas un objectif souhaitable à mon avis. (En effet, j'ai tendance à penser que le rôle d'un langage de programmation ne devrait pas être de faire quoi que ce soit d'ennuyeux - juste _clear_, ce qui n'est pas la même chose.)

Bien que je me méfie toujours d'utiliser let et var ensemble (tout comme Javascript, mais 100% différent), ce serait beaucoup moins un problème s'il y avait un passage de charpie pour détecter les variables qui sont déclarés mutables mais jamais réellement mutés.

Il existe un plan pour faire de la mutabilité une partie du type. Cela affecte-t-il les habitants et rend-il cela inutile ?

Que diriez-vous:

val x = ... // immutable (val-ue)
var y = ... // mutable (var-iable)

Comme à Scala.

Je pense que @brson a raison et que ce problème disparaît une fois que nous avons déplacé mut dans un type, c'est-à-dire que vous obtenez let x = mut 10;

Fermeture de ce problème pour l'instant ; réouvrez si vous pensez que je me trompe !

Je ne suis pas certain de cela. J'aime l'idée de déplacer le mut dans le type, mais je ne sais pas si c'est un "accord conclu" --- il peut y avoir une étrangeté persistante là-dedans. En tout cas, je n'ai jamais pensé que l'on pouvait écrire let x = mut 5 , j'ai toujours supposé que tu écrirais let mut x = 5 comme aujourd'hui ; la "mutabilité" du type d'une variable proviendrait de la manière dont elle a été déclarée, et non de la valeur qui lui est attribuée.

Faire autrement semblerait impliquer que si vous avez un tableau x de type [mut int] et que vous écrivez let y = x[0] alors y est mutable ? Ou quelque chose? Cela semble indésirable.

@Dretch Je n'aime pas val/var car ils ne sont pas assez distincts, bien que le précédent Scala soit agréable.

Je partage l' inquiétude de var par défaut. La façon dont cela fonctionne maintenant, j'ai tendance à tout déclarer comme immuable, puis le compilateur me rappelle qu'il devrait être mutable, puis je tape mut . C'est sans doute un bon comportement auquel vous seriez moins enclin avec un fractionnement de var/let - taper var et let est tout aussi difficile mais vous ne pouvez même pas taper let mut sans taper let .

Mais je n'ai pas de préférence et j'apprécie quand je peux composer des fonctions entièrement à partir d'instructions commençant par des mots-clés à trois caractères.

@nikomatsakis En particulier, il me semble logique que les règles concernant l'affectation unique proviennent de la mutabilité de la déclaration plutôt que du type. Changer subtilement les règles d'affectation en fonction du type me sent drôle.

Je suis enclin à être d'accord avec @pcwalton que nous ne devrions pas pénaliser les programmeurs d'utiliser une liaison mutable si c'est ce qu'ils veulent. En ce qui concerne la préoccupation concernant les personnes utilisant inutilement var , nous pourrions ajouter un avertissement facultatif qui se plaint si une liaison var est attribuée individuellement. Mais je pense aussi que nous pouvons créer des précédents pour un bon style dans rustc et la bibliothèque standard.

Dave

Est-ce vraiment si terrible si les programmeurs déclarent toutes leurs variables mutables ? Il semble que ce ne soit pas la fin du monde si nous avons un ensemble de programmeurs Rust qui pensent simplement que var est la façon dont vous déclarez les variables, et un autre ensemble qui comprend l'utilisation de let plupart du temps et var au besoin. En tant que premiers programmeurs de Rust, nous pouvons créer un précédent en faveur de l'utilisation correcte de let et var .

À mon humble avis, une bonne conception de la syntaxe ne consiste pas seulement à rendre pratique "tout" que vous pourriez vouloir faire, il s'agit de pousser doucement les gens sur le "chemin fluide" de la sémantique et des objectifs de conception du langage.

Par exemple, vous n'ajouteriez probablement pas de support syntaxique spécial pour les listes chaînées dans Rust (à la Haskell), car l'un des principes fondamentaux de Rust est d'être efficace, et l'utilisation généralisée des listes chaînées fonctionnera contrairement à ce principe. Pour la même raison, le partage de données mutables entre les threads ne devrait probablement pas être trop pratique (puisque la sécurité simultanée est un autre principe), et il ne devrait pas non plus être super pratique de convertir un entier arbitraire en un pointeur (puisque la sécurité de la mémoire est un grand principe).

Cela ne veut pas dire qu'il devrait être impossible de faire l'une de ces choses, attention, juste proportionnellement gênant pour qu'il ressorte clairement de la syntaxe qui est la façon idiomatique d'écrire Rust.

Les variables mutables (locales) ne sont pas aussi mauvaises que celles-ci, mais si Rust privilégie effectivement les données immuables pour des raisons d'exactitude et de maintenance (ce avec quoi je suis personnellement d'accord), alors la syntaxe devrait idéalement donner un léger coup de pouce dans cette direction. Même un seul caractère supplémentaire ou un sigil de modificateur supplémentaire ou quoi que ce soit d'autre suffirait à faire comprendre que "let" est moins compliqué que "let mut" ou "let!" ou autre, et doit donc être la valeur par défaut préférée que vous devriez essayer lorsque vous n'avez pas réellement besoin que la variable soit mutable.

@ssylvan Oh, je comprends ce point, c'est juste une question de degré et d'équilibre des compromis. Nous favorisons déjà l'immuabilité des structures de données, et les locaux immuables de l'OMI sont moins importants à promouvoir que les champs immuables. (D'autant plus que, INM, nous n'autorisons pas les locaux mutables à s'échapper dans les fermetures de tas.) Et la perte de la possibilité de refactoriser entre let et var sans changer le nombre de colonnes l'emporte sur l'avantage de promouvoir des locaux immuables. Difficile à quantifier, donc je suppose que c'est juste mon sentiment.

Dave

Eh bien, dans ce cas, au moins "let foo = mut bar" ou "let foo := bar" par opposition à "let mut foo = bar" ferait la première ligne de jetons. Vraisemblablement, le nom de la variable sera de longueur variable, il n'est donc pas si important d'éviter des modificateurs supplémentaires sur le reste de l'instruction.

Oh hé, je suis un peu partisan de l'idée := .

Dave

À la réflexion, Pascal n'est pas cool en permanence. Je reprend cela. :)

Dave

De plus, let foo := bar empêche quelque chose comme ceci :

let mut foo;
foo = bar;

J'ai parfois trouvé ce modèle utile, bien qu'il semble qu'il y ait probablement toujours une autre façon d'écrire le même modèle.

@eholk Je ne pense pas que cela empêche cela. Mais je pense toujours que cela semblera trop étrange aux programmeurs de presque tous les langages courants.

Dave

En ce qui concerne := , Go l'utilise pour indiquer une affectation de type inférentiel (bien que ce ne soit pas encore tout à fait un langage courant). Mais je peux prévoir la difficulté de distinguer entre les deux formes d'un coup d'œil :

let foo = "hello";
let foo := "hell";

L'argument de brson pour la syntaxe actuelle (c'est-à-dire que la déclaration mutable requiert d'abord la déclaration immuable) est convaincant. C'est totalement génial si les langages de programmation sont opiniâtres, du moment qu'ils ne sont pas des imbéciles à ce sujet. :)

Pas intéressé par = contre := . Principalement opposé à val , var et leurs variantes ; il n'est tout simplement pas _évident_ qu'il contrôle la mutabilité. Je veux dire, je n'arrêterai pas de dégoût si nous adoptons l'un d'entre eux, mais je pense que "avoir besoin d'expliquer le mnémonique" est un mauvais signe. Je suis plus d'accord avec :

  • Laisser le type le dicter.
  • Laisser mut seul fonctionner comme un déclarant local et obliger l'analyseur à retarder la validation de la syntaxe d'enregistrement par rapport à la déclaration locale d'un jeton supplémentaire ; c'est toujours LL (1) qui ajoute juste un état de grammaire intermédiaire supplémentaire, pas de retour en arrière supplémentaire (les deux manières d'avancer sont valides).
  • Le laisser comme let mut .

La principale raison pour éviter les locaux mutables « accidentels » est que nous avons introduit la capture de l'environnement, de sorte qu'ils se transforment en une forme d'action à distance, ainsi que des dangers pour une variété d'analyses comme l'emprunt.

(Tous les let étaient initialement mutables, mais nous n'avions pas non plus de capture d'environnement, seulement une liaison. Maintenant, nous n'avons plus de liaison, seulement une capture env. Tomayto, tomahto.)

Je pense que les mutables ne peuvent pas être capturés implicitement maintenant.

@graydon a raison de dire qu'il y avait deux motivations originales. Cependant, un seul est encore pertinent. Les deux motivations étaient

  • capture implicite "par copie" des variables mutables dans un fn@
  • comprendre quelles données sont modifiables et ce qui ne l'est pas

Il s'avère que ce dernier n'est plus pertinent. L'utilisation de variables mutables/immuables était trop grossière dans la pratique, donc loanck a l'idée d'emprunter une variable "temporairement" --- une variable mutable peut être empruntée avec un ptr immuable tant que la variable n'est pas modifiée pendant que le pointeur est dans portée.

Nous pourrions peut-être simplement supprimer l'idée de locaux mutables/immuables et revenir à l'ancienne règle --- tout est mutable. Nous pourrions alors émettre des avertissements lorsqu'une variable qui est implicitement copiée dans une fermeture est modifiée après la création de la fermeture.

Il y a une troisième motivation : les variables immuables sont plus faciles à raisonner. Si tout est modifiable, vous devez parcourir toute la fonction pour voir quelles valeurs une variable peut avoir au cours de sa durée de vie. Chaque variable a potentiellement un flux de données compliqué (en particulier avec des boucles, des branches, des paramètres de fonction mutables, etc.) et il est difficile de voir ce qui se passe sans analyser soigneusement chaque instruction. Si vous n'avez qu'un ou deux mutables dans une fonction, cela agit en quelque sorte pour les "marquer" afin que vous soyez plus prudent lors de la lecture du code impliquant hem.

@Dretch J'aime aussi le style Scala, avec les mots clés "val" et "var".

J'aime la façon dont la syntaxe actuelle fait ressortir les mutables comme un pouce endolori; cela facilite la numérisation du code. val et var semblent trop similaires visuellement à cet égard. Ce qui ne veut pas dire que les mutables doivent absolument ressortir, mais garder les mots-clés visuellement distincts est un aspect important de la convivialité.

Je comprends que les champs mutables vont être retirés de la rouille. Cela signifie-t-il que mut pourrait alors être utilisé à la place de let mut car l'ambiguïté enregistrement/variable dans le bloc disparaîtrait ?

De plus, je pense que les enregistrements structurels sont en cours, ce qui supprime l'ambiguïté même si des champs mutables restent.

@Dretch, il est vrai que les champs mutables sont en voie de disparition et que les enregistrements structurels ont déjà disparu.

Je suis généralement indifférent au problème, même si je voudrais souligner qu'il _pourrait_ être logique que mut soit un mot-clé de déclaration à part entière (comme le propose Dretch), dans le cas du gel/dégel . Comparez aujourd'hui :

let foo = 1;  // immutable
/* 10,000 lines of code here */
let mut foo = foo;  // we're making foo mutable, totally understandable
/* 10,000 lines of code here */
let foo = foo;  // potential wtf

Avec la proposition :

let foo = 1;  // immutable
/* 10,000 lines of code here */
mut foo = foo;  // a mutable foo, no problems here
/* 10,000 lines of code here */
let foo = foo;  // slightly less of a potential for wtf, since we officially have two declaration forms

Bien que je pense aussi que cela rendrait le Rust-ism "l'absence de mut implique l'immutabilité" moins cohérent, car nous écririons toujours let foo = 1; plutôt que juste foo = 1; (cette dernière forme est évidemment indésirable pour la déclaration).

Kotlin utilise également val et var .

Je ne pense pas que nous allons faire ce changement, mais je nommerai pour le jalon 1, bien défini, afin que nous puissions le régler.

le consensus est de ne pas le faire, car c'est incompatible avec le déplacement de mut vers les liaisons de motifs. fermeture.

Cette page vous a été utile?
0 / 5 - 0 notes