v
v copied to clipboard
Const in V can be modified
const should_not_change = Employee{ "Ohh" }
struct Employee {
mut:
name string
}
fn main() {
println(should_not_change)
mut arr := []&Employee{}
arr << &should_not_change
arr[0].name = "Bhak be"
println(should_not_change)
}
Expected the compilation to fail, however compiled and ran output:
$ v run main.v
Employee{
name: 'Ohh'
}
Employee{
name: 'Bhak be'
}
I understand this is probably meant as a "gotcha" type of post, but the logic behind the code is weird. You made your struct mutable, so assigning the struct to const is not going to stop you from being able to change values for the fields in the struct. Also, it isn't promised that you can't do bizarre things to const, through an intermediate and use of a reference/pointer.
Why would you expect the language to stop you from changing a mutable struct that you have a reference to? Probably, at best, I kind of think there should be just a warning about such actions. But maybe that's just me, so will be interested to see what others think.
Well I get your point @Wajinn but IMO it shouldn't be allowed. In fact, while this code is weird and not common it's still a trick to modify something that shouldn't be changed. I do think that being a const should overcome the fact that it's a mutable property. V agrees too, look at this simpler version that do not work:
const should_not_change = Employee{'Ohh'}
struct Employee {
mut:
name string
}
fn main() {
println(should_not_change)
mut a := &should_not_change
a.name = 'Bhak be'
println(should_not_change)
}
a.v:11:11: error: `should_not_change` is immutable, cannot have a mutable reference to it
9 | println(should_not_change)
10 |
11 | mut a := &should_not_change
| ^
12 |
13 | a.name = 'Bhak be'
- I believe the outcome is correct. You have a
constant pointer to holder, butnot constant holder. - Compiler is correctly identifying the possible path of modification to the pointer
should_not_changehence raising error. - But with struct, it is explicitly mentioned for it to be mutable.
Edit: On second look, it does seem wrong. Reason being compiler itself being confused. In @Hunam6's example, compiler actively stopped from creating a mutable reference to struct, thus, tried enforcing immutability. But @pbspbsingh 's example clearly shows compiler allowing (missing) the same to catch.
cc: @pbspbsingh @Hunam6
Well I get your point @Wajinn but IMO it shouldn't be allowed. In fact, while this code is weird and not common it's still a trick to modify something that shouldn't be changed...
Where are we going with this logic? It appears to lead to consts can't have structs with mutable fields, else "tricks" as the OP has posted. I'm fine if that's where it goes, but is that the consensus and best choice?
Of course an intermediate step, would be to not allow consts containing structs with mutable fields to be appended to mutable arrays by reference. But, I don't think that stops further "tricks" of the type that the OP or others may present to us in the future. Though it would be the more obvious fix.
Another related issue about that is the programmer is changing the default behavior to make such struct fields mutable and using a reference to them. The actions involved in the "trick" are clearly deliberate, thus it could be argued that the consequences should be their responsibility. This is why I leaned towards just a compiler warning, though can understand if the consensus is not allow.
V allows the ultimate "trick" - just put the code in an unsafe block, and you can do whatever you want.
Personally, I would have made it much harder than that, but... it is what it is, and is here to stay.
V allows the ultimate "trick" - just put the code in an
unsafeblock, and you can do whatever you want.
Another one, at least V has "const structs", as far as I know this isn't possible in Go (at least not directly). So as far as I'm concerned, this is a good problem to have. As with anything, when you have additional power, have to be careful about using it. I'm confident that team V will make the appropriate decisions.
As mentioned by @JalonSolov, as we have this "ultimate trick" if that's really needed by the dev, those "simple tricks" shouldn't be allowed, V should be as strict as possible.
If I may add to the discussion, basically this trick allows to have a global mutable state, which in turn will allow functions to become not pure anymore (as stated as one of the V's feature).
...allow functions to become not pure anymore (as stated as one of the V's feature).
You might want to re-read V's updated documentation and website.
...this trick allows to have a global mutable state...
I'm not saying that the "trick" shouldn't be looked at and debated about by team V, but we should be clear that global variables have been possible for users for a long time (if not from almost the beginning) via compiler flag -enable-globals and with __global. Including, though not the same thing, V also contains the struct + receiver method concept. Where a function can call a method of a struct with mutable fields, and where state can be stored and mutated.
The documentation also and still states that V is not a purely functional language. Don't think people would so rigidly attempt to use V as if it were. I think a better argument would be specific to whether or not and under any circumstances should a const value be allowed to change. Interestingly enough, I do believe there are languages that allow it, in various kinds of ways.
But, back to the V case... When a const contains a struct with a mutable field accessed by reference, we could be asking our program to do 2 possibly conflicting things: A) Don't let the value of const change. B) Allow me to change the value of the mutable field in the struct contained by const. Looking at that, I'm a bit on the side that the programmer should probably know better to not do such a thing or they are intentionally trying to pull off something wonky. But, maybe they can get themselves into that circumstance by accident.
I think that is possibly why Go completely bypassed and skipped over "const structs". However, I think V having the additional functionality and allowing it is great. It's more a matter of how team V wants to handle such a possible dilemma. I don't see it as anything nefarious or sly in regards to V's stated features or as stated in its documentation.
This is really similar to C consts - you can have a const pointer, but what it points to can change. C has always worked that way, and const structs with mutable fields is similar in concept. The struct itself is constant (as in the structure metadata), but values of fields can still be mutable.
The only way it might make sense if if your struct didn't have an mutable fields. A const of that struct really should be immutable (as long as you don't mess with pointers and unsafe code).
I think it's an issue, and I've already implemented a fix. Need to update the tests, and will push today.
you can have a const pointer, but what it points to can change.
My vision for V's consts is more strict than this.
you can have a const pointer, but what it points to can change.
My vision for V's consts is more strict than this.
I agree, prepending the const keyword should override any specific mutable behaviour of the left-hand expression.
I think it's an issue, and I've already implemented a fix. Need to update the tests, and will push today.
@medvednikov What happened to the fix? Output is the same with latest V as of today...
It is still not fixed (tried for version 0.4.6 add1621) @medvednikov did You change your mind how const struct should work?
@mac-hel I'll fix it asap.
this is still not fixed:
struct Foobar {
mut:
foo int
}
const foobar_const = Foobar{123}
fn main() {
println("foobar_const.foo: ${foobar_const.foo}") // foobar_const.foo: 123
mut foobars := []&Foobar{}
foobars << &foobar_const
foobars[0].foo = 456
println("foobar_const.foo: ${foobar_const.foo}") // foobar_const.foo: 456
}
V 0.4.10 dbc4071
Fixed:
https://github.com/vlang/v/blob/master/vlib/v/checker/tests/ban_const_ref_mutation.out