问题 #1273 建议let mut
用于可变本地变量, mut
用于可变字段。 let mut
比单个关键字更冗长,并且还会破坏列对齐。 人们理所当然地不喜欢var
用于可变字段声明的想法。 但我认为没有人建议将var
用于可变局部声明,将mut
用于可变字段声明:
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
戴夫
我不得不承认,我一开始认为我们应该对可变变量和可变字段使用相同的关键字,但是var
确实读起来还不错(虽然我并不讨厌let mut
)
我认为声明可变变量比不可变变量稍微麻烦一点。 我可以看到程序员只使用“var”来“一致”,或者因为“var”会更强大(如果你决定以后改变,不必改变它)。 然后你最终会得到整体可读性较差的代码。
并不是说 Rust 应该是一种严肃的束缚和纪律语言,但是在正确的方向上轻轻推动在道德上是合理的,我认为(即规则是“对于安全代码,更安全的构造应该比功能更强大但更容易搞砸的结构”)。
一方面我喜欢这个,因为它会消除这种形式的视觉歧义:
let mut x = 4,
y = 8; // is y mutable or not?
但我倾向于支持 ssylvan。 声明可变变量并不完全需要_丑陋_,但我认为如果它们的创建比不可变变量稍微不那么方便,如果只是通过一次击键的话,这是有道理的。 Activation 关键字也需要与众不同,并且 (IMO) var
被广泛用作通用变量声明关键字,无法在以默认不变性为荣的语言中专门传达可变性。 想想可怜的 C# 程序员,对他们来说var
就是我们的let
!
我仍然喜欢用单个关键字替换let mut
的想法,以便解决上面的代码片段,但是当mut
本身就足够时,是否有任何理由引入新关键字,如#1273 中的原始提案?
@pcwalton指出了仅使用mut
:记录文字和块表达式存在歧义,需要任意前瞻才能解决。
{ mut x: int ...
记录文字,还是使用局部变量块?
我可以看到很多程序员只是在学习var
是在 Rust 中声明变量的方式,而根本不使用let
。 毕竟, var
是您在 JavaScript 等语言中声明局部变量的方式。 我倾向于认为这是关于该提案的一件好事。
我昨天无意中听到有人指出,现在let
和var
将具有相同的长度,这将有助于对齐。
我有点冷漠,但我更喜欢var
,因为它更短。 在我看来,让可变性变得烦人并不是一个理想的目标。 (事实上,我倾向于认为编程语言的作用不应该是让任何事情变得烦人——只是_clear_,这不是一回事。)
虽然我仍然对一起使用let
和var
持谨慎态度(就像 Javascript,但 100% 不同),但如果有 lint 传递来检测变量,问题就会小得多被声明为可变的但从未真正发生过变异。
有一个计划使可变性成为类型的一部分。 这是否会影响当地人并使其变得无关紧要?
怎么样:
val x = ... // immutable (val-ue)
var y = ... // mutable (var-iable)
就像在 Scala 中一样。
我认为@brson是对的,一旦我们将mut
到一个类型中,这个问题就会消失,即你得到let x = mut 10;
暂时关闭此问题; 如果您认为我错了,请重新打开!
我不确定这一点。 我喜欢将 mut 移到类型中的想法,但我不知道这是“完成的交易”——其中可能有挥之不去的怪异之处。 无论如何,我从来没有想过有人会写let x = mut 5
,我一直认为你会像今天一样写let mut x = 5
; 变量类型的“可变性”将来自它的声明方式,而不是分配给它的值。
否则似乎意味着如果您有一个x
类型的数组[mut int]
并且您编写let y = x[0]
那么y
是可变的吗? 或者其他的东西? 这似乎是不可取的。
@Dretch我不喜欢 val/var,因为它们不够明显,尽管 Scala 的先例很好。
我分享@eholk对人们学习默认使用var
的担忧。 它现在的工作方式我倾向于将所有内容声明为不可变的,然后编译器提醒我它应该是可变的,然后我输入mut
。 这可以说是很好的行为,您不太倾向于使用var/let
拆分 - 输入var
和let
同样困难,但您甚至无法输入let mut
不输入let
。
但是我没有偏好,当我可以完全由以三个字符的关键字开头的语句组成函数时,我确实很感激。
@nikomatsakis特别是,关于单赋值的规则应该来自声明的可变性而不是类型,这对我来说很有意义。 根据类型巧妙地更改分配规则对我来说很有趣。
我倾向于同意@pcwalton 的观点,即如果程序员想要使用可变绑定,我们不应该惩罚他们。 至于人们不必要地使用var
的担忧,我们可以添加一个可选的警告,如果var
绑定是单独分配的,则该警告会抱怨。 但我也认为我们可以为 rustc 和标准库中的良好风格树立先例。
戴夫
如果程序员声明他们的所有变量都是可变的,真的有那么可怕吗? 如果我们有一组 Rust 程序员认为var
是你声明变量的方式,而另一组大多数时间理解使用let
,这似乎不是世界末日和var
需要时。 作为第一批 Rust 程序员,我们可以树立正确使用let
和var
的先例。
恕我直言,好的语法设计不仅仅是让您可能想做的“一切”变得方便,而是要轻轻地将人们推向语言语义和设计目标的“平滑路径”。
例如,您可能不会在 Rust(a la Haskell)中添加对链表的特殊语法支持,因为 Rust 的基本原则之一是高效,而普遍使用链表将与该原则背道而驰。 出于同样的原因,在线程之间共享可变数据可能不应该太方便(因为安全并发是另一个原则),将任意 int 强制转换为指针也不应该太方便(因为内存安全是一个大原则)。
并不是说这些事情都不应该做,记住,只是按比例不方便,所以从语法中可以清楚地看出这是编写 Rust 的惯用方式。
可变(局部)变量并不像其中任何一个那么糟糕,但如果 Rust 确实出于正确性和维护原因(我个人同意这一点)确实偏爱不可变数据,那么理想情况下,语法应该在这个方向上轻推。 即使是一个额外的字符或一个额外的修饰符印记或其他任何东西都足以表明“let”没有“let mut”或“let!”复杂。 或其他什么,因此必须是首选默认值,当您实际上不需要变量是可变的时,您应该尝试使用该默认值。
@ssylvan哦,我理解这一点,这只是程度和权衡取舍的问题。 我们已经促进了数据结构的不变性,并且 IMO 不可变局部变量比不可变字段更不重要。 (特别是因为,IINM,我们不允许可变局部变量在堆闭包中逃逸。)并且在不改变列数的情况下,在let
和var
之间进行重构的能力的损失超过了促进不变的当地人。 很难量化,所以我想这只是我的感觉。
戴夫
那么在这种情况下,至少“let foo = mut bar”或“let foo := bar”而不是“let mut foo = bar”将使第一个标记对齐。 据推测,变量名称的长度是可变的,因此避免在语句的其余部分使用额外的修饰符并不是那么重要。
哦,嘿,我有点偏爱:=
想法。
戴夫
再想一想,帕斯卡永远不酷。 我把它收回。 :)
戴夫
此外, let foo := bar
可以防止这样的事情:
let mut foo;
foo = bar;
我发现这种模式有时很有用,尽管似乎总是有另一种方式来编写相同的模式。
@eholk我不认为它阻止了这一点。 但我仍然认为对于几乎所有主流语言的程序员来说,它看起来都太奇怪了。
戴夫
关于:=
,Go 使用它来表示类型推断赋值(尽管它还不是主流语言)。 但我一眼就能预见到区分这两种形式的困难:
let foo = "hello";
let foo := "hell";
brson 对当前语法的论证(即可变声明首先需要不可变声明)是令人信服的。 如果编程语言固执己见,那就太好了,只要它们不是混蛋。 :)
对=
与:=
不感兴趣。 主要反对val
、 var
及其变体; 它控制可变性并不是_显而易见的_。 我的意思是,如果我们采用其中一个,我不会厌恶地退出,但我认为“需要解释助记符”是一个不好的迹象。 我对:
mut
单独作为本地声明符工作,并要求解析器延迟提交记录语法与本地声明一个额外的标记; 它仍然是 LL(1) 只是添加了一个额外的中间语法状态,没有额外的回溯(两种方式都有效)。let mut
。避免“意外”可变局部变量的主要原因是我们引入了环境捕获,因此它们变成了一种远距离动作形式,以及各种分析(例如借用)的危害。
(所有 let 最初都是可变的,但我们也没有环境捕获,只有绑定。现在我们没有绑定,只有环境捕获。Tomayto,tomahto。)
我相信现在不能隐式捕获可变参数。
@graydon是正确的,有两个原始动机。 然而,只有一个仍然相关。 这两个动机是
fn@
中可变变量的隐式“by-copy”捕获事实证明,后者不再相关。 可变/不可变变量在实践中的使用过于粗糙,所以borrowck 有“临时”借用变量的想法——可变变量可以用不可变的ptr 借用,只要该变量在指针在范围。
我们或许可以去掉可变/不可变局部变量的想法,回到旧规则——一切都是可变的。 然后,当在创建闭包后修改隐式复制到闭包中的变量时,我们可以发出警告。
还有第三个动机:不可变变量更容易推理。 如果一切都是可变的,您必须扫描整个函数以查看变量在其生命周期中可能具有的值。 每个变量都可能具有复杂的数据流(尤其是循环、分支、可变函数参数等),如果不仔细分析每个语句,就很难看出发生了什么。 如果您在一个函数中只有一个或两个可变变量,它会“标记”它们,以便您在阅读涉及 hem 的代码时更加小心。
@Dretch我也喜欢 Scala 风格,带有“val”和“var”关键字。
我喜欢当前的语法如何导致可变参数像拇指酸痛一样突出; 它使扫描代码更容易。 val
和var
在这方面看起来太相似了。 这并不是说可变变量绝对_必须_突出,但保持关键字在视觉上不同是可用性的一个重要方面。
我知道可变字段将从 Rust 中删除。 这是否意味着可以使用mut
而不是let mut
因为记录/块中变量的歧义会消失?
此外,我相信结构记录正在发生,即使可变字段仍然存在,这也消除了歧义。
@Dretch确实,可变字段正在消失,结构记录已经消失。
我对这个问题基本上无动于衷,尽管我想指出,在冻结/解冻的情况下, mut
本身可能是一个声明关键字(正如 Dretch 所提议的) . 今日对比:
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
随着提案:
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
虽然我也觉得这会让 Rust 主义“没有 mut 意味着不变性”不太一致,因为我们仍然会写let foo = 1;
而不仅仅是foo = 1;
(后一种形式显然是不可用于声明)。
Kotlin还使用val
和var
。
我不认为我们会做出这个改变,但我会提名里程碑 1,定义明确,所以我们可以解决它。
共识是不要这样做,因为它与将 mut 移动到模式绑定不兼容。 关闭。