Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

var keyword for mutable locals only #2643

Closed
dherman opened this issue Jun 19, 2012 · 42 comments
Closed

var keyword for mutable locals only #2643

dherman opened this issue Jun 19, 2012 · 42 comments

Comments

@dherman
Copy link
Contributor

dherman commented Jun 19, 2012

Issue #1273 proposed let mut for mutable locals and mut for mutable fields. let mut is more verbose than a single keyword and also breaks column alignment. People rightly didn't like the idea of var for mutable field declarations. But I think nobody suggested the idea of using var for mutable local declarations and mut for mutable field declarations:

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

@nikomatsakis
Copy link
Contributor

I have to confess, I at first thought that we should use the same keyword for mutable vars and mutable fields, but var does read kinda' well (though I don't dislike let mut)

@ssylvan
Copy link

ssylvan commented Jun 20, 2012

I think it's a feature that declaring a mutable variable is ever-so-slightly more cumbersome than an immutable one. I can see programmers using just "var" to be "consistent" or because "var" would be more powerful (don't have to change it if you decide to mutate later). Then you end up with less readable code overall.

Not suggesting that Rust should be a serious bondage & discipline language, but a gentle nudge in the right direction is morally justifiable, I think (i.e. the rule would be "for safe code, the safer constructs should be less noisy than the more powerful but easier-to-mess-up constructs").

@bstrie
Copy link
Contributor

bstrie commented Jun 20, 2012

On one hand I like this, since it would remove the visual ambiguity from this form:

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

But I'm inclined to side with ssylvan. Declaring mutables doesn't exactly need to be ugly, but I think it makes sense if they're ever-so-slightly less convenient to create than immutables, if only by a single keystroke. The activating keyword also needs to be distinctive, and (IMO) var is too widely used as a generic variable declaration keyword to specifically convey mutability in a language that prides itself on immutability-by-default. And think of the poor C# programmers, for whom var is our let!

I still like the idea of replacing let mut with a single keyword, so as to address the code snippet above, but is there any reason for introducing a new keyword when mut itself would suffice, as per the original proposal in #1273?

@nikomatsakis
Copy link
Contributor

@pcwalton pointed out a problem with just using mut: there is an ambiguity with record literals and block expressions that requires arbitrary lookahead to resolve.

{ mut x: int ...

Record literal, or block with local variable?

@eholk
Copy link
Contributor

eholk commented Jun 20, 2012

I could see a lot of programmers just learning that var is how you declare variables in Rust, and not using let at all. After all, var is how you declare local variables in languages like JavaScript. I'm inclined to think this is a good thing about this proposal.

I overheard someone point out yesterday that now let and var would have the same length, which would be nice for alignment.

@pcwalton
Copy link
Contributor

I'm somewhat indifferent, but I somewhat prefer var, because it's shorter. Making mutability annoying isn't a desirable goal as I see it. (Indeed, I tend to think the role of a programming language should not be to make anything annoying—just clear, which isn't the same thing.)

@bstrie
Copy link
Contributor

bstrie commented Jun 20, 2012

While I'm still wary of using let and var together (just like Javascript, but 100% different), it would be much less of an issue if there were a lint pass to detect variables that are declared as mutable but never actually mutated.

@brson
Copy link
Contributor

brson commented Jun 20, 2012

There is a plan to make mutability a part of the type. Does that affect locals and make this irrelevant?

@Dretch
Copy link
Contributor

Dretch commented Jun 20, 2012

How about:

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

Like in Scala.

@graydon
Copy link
Contributor

graydon commented Jun 20, 2012

I think @brson is right and this issue vanishes once we move mut into a type, i.e. you get let x = mut 10;

Closing this issue for now; reopen if you think I'm wrong!

@graydon graydon closed this as completed Jun 20, 2012
@nikomatsakis
Copy link
Contributor

I am not sure about this. I like the idea of moving mut into the type, but I don't know that it's a "done deal"---there may be lingering weirdness in there. In any case, I never considered that one might write let x = mut 5, I always assumed you'd write let mut x = 5 just as today; the "mutability-ness" of the type of a variable would come from the way it was declared, not the value being assigned to it.

To do otherwise would seem to imply that if you have an array x of type [mut int] and you write let y = x[0] then y is mutable? Or something? That seems undesirable.

@brson brson reopened this Jun 21, 2012
@brson
Copy link
Contributor

brson commented Jun 21, 2012

@Dretch I don't love val/var because they are not distinct enough, though the Scala precedent is nice.

I share @eholk's concern about people learning to use var by default. The way it works now I tend to declare everything as immutable, then the compiler reminds me it should be mutable, then I type mut. This is arguably good behavior that you would be less inclined to with a var/let split - typing var and let are equally difficult but you can't even type let mut without typing let.

But I don't h ave a preference and I do appreciate when I can compose functions entirely of statements beginning with three-character keywords.

@dherman
Copy link
Contributor Author

dherman commented Jun 21, 2012

@nikomatsakis In particular, it makes sense to me that the rules about single-assignment should come from the mutability of the declaration rather than the type. Subtly changing the assignment rules based on type smells funny to me.

I'm inclined to agree with @pcwalton that we shouldn't penalize programmers from using a mutable binding if that's what they want. As for the concern about people unnecessarily using var, we could add an optional warning that complains if a var binding is singly-assigned. But I also think that we can set precedents for good style in rustc and the standard library.

Dave

@eholk
Copy link
Contributor

eholk commented Jun 21, 2012

Is it really that terrible if programmers declare all their variables mutable? It seems like it's not the end of the world if we have a set of Rust programmers that just think var is how you declare variables, and another set that understands to use let most of the time and var when needed. As the first Rust programmers, we can set the precedent in favor of using let and var correctly.

@ssylvan
Copy link

ssylvan commented Jun 21, 2012

IMHO good syntax design is not just about making "everything" you might ever want to do convenient, it's about gently nudging people on to the "smooth path" of the language's semantics and design goals.

For example, you probably wouldn't add special syntactic support for linked lists in Rust (a la Haskell), because one of Rust's foundational principles is to be efficient, and pervasive use of linked lists will work contrary to that principle. For the same reason sharing mutable data between threads probably shouldn't be too convenient (since safe concurrency is another principle), nor should it be super convenient to cast an arbitrary int to a pointer (since memory safety is a big principle).

Not to say that it should be impossible to do any of these things, mind, just proportionately inconvenient so that it's clear from the syntax which is the idiomatic way to write Rust.

Mutable (local) variables aren't nearly as bad as any of these, but if Rust is indeed favouring immutable data for correctness and maintenance reasons (something I personally agree with), then the syntax should ideally give a gentle nudge in that direction. Even a single extra character or an extra modifier sigil or whatever would be enough to make it clear that "let" is less complicated than "let mut" or "let!" or whatever, and therefore must be the preferred default you should try to go for when you don't actually need the variable to be mutable.

@dherman
Copy link
Contributor Author

dherman commented Jun 21, 2012

@ssylvan Oh, I understand that point, it's just a question of degree, and the balance of trade-offs. We already promote immutability of data structures, and IMO immutable locals are less important to promote than immutable fields. (Especially since, IINM, we don't allow mutable locals to escape in heap closures.) And the loss of the ability to refactor between let and var without changing column count outweighs the benefit of promoting immutable locals. Hard to quantify, so I guess it's just my feeling.

Dave

@ssylvan
Copy link

ssylvan commented Jun 21, 2012

Well in that case at least "let foo = mut bar" or "let foo := bar" as opposed to "let mut foo = bar" would make the first token line up. Presumably the variable name will be of variable length so it's not so important to avoid extra modifiers on the rest of the statement.

@dherman
Copy link
Contributor Author

dherman commented Jun 21, 2012

Oh hey, I'm kinda partial to the := idea.

Dave

@dherman
Copy link
Contributor Author

dherman commented Jun 21, 2012

On second thought, Pascal is permanently uncool. I take it back. :)

Dave

@eholk
Copy link
Contributor

eholk commented Jun 21, 2012

Also, let foo := bar prevents something like this:

let mut foo;
foo = bar;

I've found that pattern useful at times, although it seems like there's probably always another way to write the same pattern.

@dherman
Copy link
Contributor Author

dherman commented Jun 21, 2012

@eholk I don't think it prevents that. But I still think it'll look too weird to programmers from almost any mainstream language.

Dave

@bstrie
Copy link
Contributor

bstrie commented Jun 22, 2012

Regarding :=, Go uses it to indicate type-inferential assignment (though it's not quite a mainstream language yet). But I can foresee difficulty distinguishing between the two forms at a glance:

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

brson's argument for the current syntax (i.e. that the mutable declaration first requires the immutable declaration) is convincing. It's totally great if programming languages are opinionated, just as long as they aren't jerks about it. :)

@graydon
Copy link
Contributor

graydon commented Jun 22, 2012

Not interested in = vs. :=. Mostly opposed to val, var and variations thereof; it's simply not obvious that it controls mutability. I mean, I won't quit in disgust if we adopt one of them, but I think "needing to explain the mnemonic" is a bad sign. I'm more-ok with:

  • Letting the type dictate it.
  • Letting mut alone work as a local-declarator and requiring the parser to delay committing to record-syntax vs. local-declaration one extra token; it's still LL(1) is just adds an extra intermediate grammar-state, no extra backtracking (both ways forward are valid).
  • Leaving it as let mut.

The main reason to avoid "accidental" mutable locals is that we introduced environment capture, so they turn into a form of action-at-a-distance, as well as hazards for a variety of analyses like borrowing.

(All lets were initially mutable, but we also had no environment capture, only bind. Now we have no bind, only env capture. Tomayto, tomahto.)

@brson
Copy link
Contributor

brson commented Jun 22, 2012

I believe mutables cannot be implicitly captured now.

@nikomatsakis
Copy link
Contributor

@graydon is correct that there were two original motivations. However only one is still relevant. The two motivations were

  • implicit "by-copy" capture of mutable variables in an fn@
  • understanding what data is mutable and what is not for borrowck

It turns out that the latter is no longer relevant. The use of mutable/immutable variables was too crude in practice so borrowck has the idea of borrowing a variable "temporarily"---a mutable variable can be borrowed with an immutable ptr so long as the variable is not modified while the pointer is in scope.

We could perhaps just remove the idea of mutable/immutable locals and go back to the old rule---everything is mutable. We could then issue warnings when a variable that is implicitly copied into a closure is modified after the closure is created.

@ssylvan
Copy link

ssylvan commented Jul 5, 2012

There's a third motivation: immutable variables are easier to reason about. If everything is mutable you have to scan around the whole function to see what values a variable might have during its life time. Every variable potentially has complicated data flow (esp. with loops, branches, mutable function parameters, etc.) and it's hard to see what's going on without carefully analyzing every statement. If you have only one or two mutables in a function it sort of acts to "flag" them so that you're more careful when reading code involving hem.

@lilac
Copy link
Contributor

lilac commented Jul 6, 2012

@Dretch I also like the Scala style, with "val" and "var" key words.

@bstrie
Copy link
Contributor

bstrie commented Jul 6, 2012

I like how the current syntax causes mutables to stick out like a sore thumb; it makes scanning the code easier. val and var seem too visually similar in that regard. Which is not to say that mutables absolutely must stick out, but keeping keywords visually distinct is an important facet of usability.

@Dretch
Copy link
Contributor

Dretch commented Feb 24, 2013

I understand that mutable fields are going to be removed from rust. Does that mean that mut could then be used instead of let mut because the record/variable-in-block ambiguity would disappear?

@Dretch
Copy link
Contributor

Dretch commented Mar 6, 2013

Also I believe structural records are going, which removes the ambiguity even if mutable fields remain.

@bstrie
Copy link
Contributor

bstrie commented Mar 7, 2013

@Dretch it's true that mutable fields are on their way out, and structural records are already gone.

I'm mostly indifferent to the issue, although I would like to point out that it might make sense for mut to be a declaration keyword in its own right (as Dretch proposes), in the case of freezing/thawing. Compare today:

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

With the proposal:

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

@bstrie
Copy link
Contributor

bstrie commented Mar 7, 2013

Though I also feel like this would make the Rust-ism "absence of mut implies immutability" less consistent, because we'd still be writing let foo = 1; rather than just foo = 1; (the latter form is obviously undesirable for declaration).

@chocolateboy
Copy link

Kotlin also uses val and var.

@catamorphism
Copy link
Contributor

I don't think we're going to make this change, but I'll nominate for milestone 1, well-defined, so we can settle it.

@graydon
Copy link
Contributor

graydon commented Jun 6, 2013

consensus is to not do this, as it's incompatible with moving mut to pattern bindings. closing.

@Jeklah
Copy link

Jeklah commented Apr 2, 2024

I've just come across this bit of code...

var out: VertexOutput;
let x = f32(1 - i32(in_vertex_index)) * 0.5;
let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5;  

and this is the first time I've come across var in rust.
From reading this thread, var is the same as let mut..

So the above code could be re-written as:

let mut out: VertexOutput;

, correct? Thanks all just looking for clarification.

@bstrie
Copy link
Contributor

bstrie commented Apr 2, 2024

@Jeklah I'm curious, where did you come across this code? Any Rust code using var must be so old that it would be considered positively prehistoric by this point. You are correct that let mut is the modern equivalent of declaring a mutable variable, although if the code that you're reading is that old then I suspect this will only be the first of many strange things that you will encounter. :)

@Jeklah
Copy link

Jeklah commented Apr 2, 2024

@Jeklah I'm curious, where did you come across this code? Any Rust code using var must be so old that it would be considered positively prehistoric by this point. You are correct that let mut is the modern equivalent of declaring a mutable variable, although if the code that you're reading is that old then I suspect this will only be the first of many strange things that you will encounter. :)

It was in a wgpu tutorial.

I was thinking I think id prefer to use let mut instead of var just for uniformity and less of people asking "what's var for when we have let mut?"

Makes more sense var is an old thing. Thanks for the heads up.

@Pauan
Copy link

Pauan commented Apr 2, 2024

It was in a wgpu tutorial.

The code you posted isn't Rust code at all, it's wgsl code, which is a completely different language with different syntax and behavior.

@Jeklah
Copy link

Jeklah commented Apr 2, 2024

It was in a wgpu tutorial.

The code you posted isn't Rust code at all, it's wgsl code, which is a completely different language with different syntax and behavior.

Interesting as I'm running it with cargo.

@Pauan
Copy link

Pauan commented Apr 2, 2024

Interesting as I'm running it with cargo.

I assume you're using the include_wgsl! macro which imports a .wgsl file.

Even though include_wgsl! is a Rust macro, and it's compiled with cargo, the wgsl code is still not Rust.

Rust macros are able to include non-Rust code, such as typed-html including HTML code, or include-sql including SQL code.

@Jeklah
Copy link

Jeklah commented Apr 3, 2024

Interesting as I'm running it with cargo.

I assume you're using the include_wgsl! macro which imports a .wgsl file.

Even though include_wgsl! is a Rust macro, and it's compiled with cargo, the wgsl code is still not Rust.

Rust macros are able to include non-Rust code, such as typed-html including HTML code, or include-sql including SQL code.

That is correct. I'm following a tutorial on using rust with wgsl.

celinval pushed a commit to celinval/rust-dev that referenced this issue Jun 4, 2024
`goto-synthesizer` should take the same backend options as `cbmc` to
make sure that the checker we used to check loop-contracts candidates is
the same as the checker we verify the final goto-binary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests