www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - AA and struct with const member

reply frame <frame86 live.com> writes:
I feel stupid right now: One cannot assign a struct that contains 
const member to AA?

Error: cannot modify struct instance ... of type ... because it 
contains `const` or `immutable` members

This is considered a modification?
```d
struct S
{
   const(int) a;
}

S[string] test;
test["a"] = S(1);
```

Whats the workaround for that?
Dec 27 2021
parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Monday, 27 December 2021 at 19:38:38 UTC, frame wrote:
 I feel stupid right now: One cannot assign a struct that 
 contains const member to AA?

 Error: cannot modify struct instance ... of type ... because it 
 contains `const` or `immutable` members

 This is considered a modification?
 ```d
 struct S
 {
   const(int) a;
 }

 S[string] test;
 test["a"] = S(1);
 ```

 Whats the workaround for that?
const/immutable members are to be set/assigned instantiation. Most likely the problem is a bug and sounds like a) the struct doesn't exist in the AA, so it creates it (with a default) b) It tries to copy but contains a const and thus fails Passing a pointer will do you no good, since structs are likely to be on the stack. So let's try opAssign. ```d auto ref opAssign(S s) { this=s; return this; } ``` So we get ``` 'cannot modify struct instance `this` of type `S` because it contains `const` or `immutable` members'. ``` Alright let's look at the members we can work with. https://dlang.org/spec/hash-map.html I don't see an 'add' but i do see a 'require' which will add something in. So we try that. test.require("a", S(1)); ``` Now we get: Error: cannot modify struct instance `*p` of type `S` because it contains `const` or `immutable` members test.d(??): Error: template instance `object.require!(string, S)` error instantiating ``` Hmmm it really doesn't like it. Finally we can fake it. Let's make a mirror struct without the const, for the purposes of adding it. ```d struct S { const(int) a; } struct S2 { int a; } S[string] test; cast(S2[string])test = S2(1); ``` ``` Error: `cast(S2[string])test` is not an lvalue and cannot be modified ``` Well that's not going to work. Let's make it a pointer and allocate it instead. ```d S*[string] test; test["a"] = new S(1); ``` Success! So i summarize, either work with a pointer, or drop the const...
Dec 27 2021
next sibling parent reply Tejas <notrealemail gmail.com> writes:
On Tuesday, 28 December 2021 at 01:45:42 UTC, Era Scarecrow wrote:
 On Monday, 27 December 2021 at 19:38:38 UTC, frame wrote:
 [...]
const/immutable members are to be set/assigned instantiation. Most likely the problem is a bug and sounds like [...]
The workaround is okay, but I think we should file a bug report for this. This is very ~~stupid~~ undesirable behaviour
Dec 27 2021
parent reply frame <frame86 live.com> writes:
On Tuesday, 28 December 2021 at 06:38:03 UTC, Tejas wrote:
 On Tuesday, 28 December 2021 at 01:45:42 UTC, Era Scarecrow 
 wrote:
 On Monday, 27 December 2021 at 19:38:38 UTC, frame wrote:
 [...]
const/immutable members are to be set/assigned instantiation. Most likely the problem is a bug and sounds like [...]
The workaround is okay, but I think we should file a bug report for this. This is very ~~stupid~~ undesirable behaviour
I agree. I'll just wait if somebody can explain why this isn't a bug or wanted behaviour or a known issue.
Dec 27 2021
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 28 December 2021 at 07:54:56 UTC, frame wrote:
 On Tuesday, 28 December 2021 at 06:38:03 UTC, Tejas wrote:
 The workaround is okay, but I think we should file a bug 
 report for this.
 This is very ~~stupid~~ undesirable behaviour
I agree. I'll just wait if somebody can explain why this isn't a bug or wanted behaviour or a known issue.
[The spec says](https://dlang.org/spec/hash-map.html#construction_assignment_entries): ``` 2. If the assigned value type is equivalent with the AA element type: 1. If the indexing key does not yet exist in AA, a new AA entry will be allocated, and it will be initialized with the assigned value. 2. If the indexing key already exists in the AA, the setting runs normal assignment. ``` Thus, when the value type is constructable but not assignable: ```d struct S { const(int) a; } void test(S[string] aa, string key, int value) { // Should be a compile-time error, because it might reassign: test[key] = S(value); // Should be accepted, because they can be proven at compile time to never reassign: test.require("a", S(value)); test.update("a", () => S(value), (ref const(S)) => { }); } ``` `require` and `update` can be fixed rather easily in `object.d`; I have submitted [issue 22633](https://issues.dlang.org/show_bug.cgi?id=22633) with sample code.
Dec 28 2021
parent reply frame <frame86 live.com> writes:
On Tuesday, 28 December 2021 at 10:02:13 UTC, tsbockman wrote:

   // Should be a compile-time error, because it might reassign:
   test[key] = S(value);
This might be a typo in your example but why should it be a compile-time error, it cannot know if the key already exists in compile time on a variable. First time initialization should always work anyway.
 `require` and `update` can be fixed rather easily in 
 `object.d`; I have submitted [issue 
 22633](https://issues.dlang.org/show_bug.cgi?id=22633) with 
 sample code.
Perfect. Thank you.
Dec 28 2021
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Tuesday, 28 December 2021 at 22:46:16 UTC, frame wrote:
 On Tuesday, 28 December 2021 at 10:02:13 UTC, tsbockman wrote:

   // Should be a compile-time error, because it might reassign:
   test[key] = S(value);
This might be a typo in your example but why should it be a compile-time error, it cannot know if the key already exists in compile time on a variable. First time initialization should always work anyway.
Because opIndexAssign cannot distinguish at compile time between initialization and assignment: ```d Stuff[Key] aa; aa[key] = Stuff(args); // ostensibly, initialization aa[key] = otherStuff; // assignment to existing value ``` Same syntax, different behavior. This can only be caught at runtime. `require` and `update` though should be able to pull this off, and that they don't is a bug.
Dec 28 2021
next sibling parent reply frame <frame86 live.com> writes:
On Wednesday, 29 December 2021 at 01:11:13 UTC, Stanislav Blinov 
wrote:

 Because opIndexAssign cannot distinguish at compile time 
 between initialization and assignment:

 ```d
 Stuff[Key] aa;
 aa[key] = Stuff(args); // ostensibly, initialization
 aa[key] = otherStuff;  // assignment to existing value
 ```

 Same syntax, different behavior. This can only be caught at 
 runtime. `require` and `update` though should be able to pull 
 this off, and that they don't is a bug.
Of course but opIndexAssign() isn't there in my example. The compiler should call runtime's _aaGetY() or something like that directly.
Dec 28 2021
parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Wednesday, 29 December 2021 at 02:33:08 UTC, frame wrote:
 On Wednesday, 29 December 2021 at 01:11:13 UTC, Stanislav 
 Blinov wrote:

 Because opIndexAssign cannot distinguish at compile time 
 between initialization and assignment:

 ```d
 Stuff[Key] aa;
 aa[key] = Stuff(args); // ostensibly, initialization
 aa[key] = otherStuff;  // assignment to existing value
 ```

 Same syntax, different behavior. This can only be caught at 
 runtime. `require` and `update` though should be able to pull 
 this off, and that they don't is a bug.
Of course but opIndexAssign() isn't there in my example. The compiler should call runtime's _aaGetY() or something like that directly.
It is doing that. You've asked why that should be compile error, and the answer is - because there's no way to distinguish between initialization and assignment here. I.e. you can't make one line compile and the other - not. Either both compile, or both don't. So if you allow them to compile, you'll have to have a runtime check, throwing an exception on attempt to assign. Which is just horrible. Better to leave the assignment a compile error, and make `require` and `update` do the job they're supposed to be doing.
Dec 28 2021
prev sibling parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Wednesday, 29 December 2021 at 01:11:13 UTC, Stanislav Blinov 
wrote:
 Because opIndexAssign cannot distinguish at compile time 
 between initialization and assignment:

 ```d
 Stuff[Key] aa;
 aa[key] = Stuff(args); // ostensibly, initialization
 aa[key] = otherStuff;  // assignment to existing value
 ```

 Same syntax, different behavior. This can only be caught at 
 runtime. `require` and `update` though should be able to pull 
 this off, and that they don't is a bug.
So i wonder if const and immutable would have different behaviors then. While you shouldn't be able to explicitly change a const item within a struct, replacing the whole struct i would think would be okay, on the basis that you're basically throwing whole old item away (*and may be equal to what you'd do with say swap*). Immutable on the other hand may want to refuse as it should basically have a lifetime of the array? Though if you can delete the item and then just add it in again it's a longer version of the same thing, just depends on if anything is using/referencing it or not. And casting away the constness is easy enough so maybe it won't be different. Though if it's a basic type it seems unlikely it would need a longer lifetime, and if it's a reference or array it already is separated from the struct and needs no such protection for the pointer. I don't know. I remember odd behavior with const/non-const stuff before.
Dec 29 2021
prev sibling parent reply frame <frame86 live.com> writes:
On Tuesday, 28 December 2021 at 01:45:42 UTC, Era Scarecrow wrote:

  Success!

  So i summarize, either work with a pointer, or drop the 
 const...
Of course casting the const away was the first thing I did but I think this is not very clean :D
Dec 27 2021
parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Tuesday, 28 December 2021 at 07:51:04 UTC, frame wrote:
 On Tuesday, 28 December 2021 at 01:45:42 UTC, Era Scarecrow 
 wrote:

  Success!

  So i summarize, either work with a pointer, or drop the 
 const...
Of course casting the const away was the first thing I did but I think this is not very clean :D
Well the next step up would be if the key does exist, you could then memcpy the result... which can have issues with non-native basic types. Probably better to make data private vs making it const. I tend to use const far more as input arguments to help denote it won't change references and less for elements in a struct. That or make it a class? I'm not sure.
Dec 28 2021
parent frame <frame86 live.com> writes:
On Tuesday, 28 December 2021 at 14:48:24 UTC, Era Scarecrow wrote:

  Probably better to make data private vs making it const. I 
 tend to use const far more as input arguments to help denote it 
 won't change references and less for elements in a struct. That 
 or make it a class? I'm not sure.
It's a matter of consistency - if a function returns something in const one should be able to store it without any type modification.
Dec 28 2021