www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - cannot modify struct with immutable members

reply ted <foo bar.com> writes:
I get the following error from the code below: (dmd2.066.1, linux)
test.d(26): Error: cannot modify struct myTest1 Test with immutable members

Is this expected?

If so, how can I achieve this result - being able to set (a new) initial value 
of myTest1 from within an nested function ?

thanks !
ted


code: 
struct A
{
    int someInt;
}

struct Test
{
    this( ref const(A) arg )
    {
        mA = arg;
    }

private:
    const(A) mA;
}

void main()
{
    Test myTest1;
    A topA;

    void _someFunc( ref const(A) myA )
    {
        myTest1 = Test(myA);
    }

    Test myTest2 = Test(topA);      // is ok as expected
    _someFunc( topA );		  // error.
}
Jan 02 2015
parent reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sat, 03 Jan 2015 13:25:31 +1030
ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

=20
 I get the following error from the code below: (dmd2.066.1, linux)
 test.d(26): Error: cannot modify struct myTest1 Test with immutable membe=
rs
=20
 Is this expected?
=20
 If so, how can I achieve this result - being able to set (a new) initial =
value=20
 of myTest1 from within an nested function ?
=20
 thanks !
 ted
=20
=20
 code:=20
 struct A
 {
     int someInt;
 }
=20
 struct Test
 {
     this( ref const(A) arg )
     {
         mA =3D arg;
     }
=20
 private:
     const(A) mA;
 }
=20
 void main()
 {
     Test myTest1;
     A topA;
=20
     void _someFunc( ref const(A) myA )
     {
         myTest1 =3D Test(myA);
     }
=20
     Test myTest2 =3D Test(topA);      // is ok as expected
     _someFunc( topA );		  // error.
 }
the question is: "why do you want `mA` to be const?" leaving aside compiler complaints this is the one and important question. let's try to fix your code instead of devising workarounds to beat the compiler. ;-) please remember that D `const` is not the same as C/C++ `const`, so my question is not so stupid as it may look. p.s. you can use `in A myA` instead of `ref const A myA`, in most cases compiler is intelligent enough to use "pass-by-reference" for `in` args.
Jan 02 2015
parent reply ted <foo bar.com> writes:
Hi,

thanks for the reply...to answer your direct question, the original code 
looked like:


struct A
{
    int someInt;
}

struct Test
{
     property { const(A) getA() { return mA; } }
    this( ref const(A) arg )
    {
        mA = arg;
    }

private:
    A mA;
}

void main()
{
    Test myTest1;
    A topA;

    void _someFunc( ref const(A) myA )
    {
        myTest1 = Test(myA);
    }

    Test myTest2 = Test(topA);      // is ok as expected
    _someFunc( topA );

    // later on....
    A foo = myTest1.getA();
}

which compiles. However, I think I exercised some other compiler-related 
bug when I added a field to another structure in the module, and the 
compiler started complaining about the construct above.......
'Error: cannot implicitly convert expression (arg) of type const(A) to A' 
in the constructor of Test (I'm unable to recreate this error in this 
simple code example).

(I am prototyping something, so the code I'm producing is very badly 
structured, and I have nested functions within delegates within .... and I 
have also hit some odd scoping errors - definite compiler issues - 
(notwithstanding the horrible code structure)).

....so (given my D knowledge is limited) I then wondered why it accepted 
(nonconst)mA=(const)arg in the first place, hence the const()...hence the 
original question.

I am assuming that the ref const(A) myA means that _someFunc (and below) is 
unable to alter any fields of myA. This may be my basic error.

thanks !






ketmar via Digitalmars-d-learn wrote:

 On Sat, 03 Jan 2015 13:25:31 +1030
 ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:
 
 
 I get the following error from the code below: (dmd2.066.1, linux)
 test.d(26): Error: cannot modify struct myTest1 Test with immutable 
members
 
 Is this expected?
 
 If so, how can I achieve this result - being able to set (a new) initial
 value of myTest1 from within an nested function ?
 
 thanks !
 ted
 
 
 code:
 struct A
 {
     int someInt;
 }
 
 struct Test
 {
     this( ref const(A) arg )
     {
         mA = arg;
     }
 
 private:
     const(A) mA;
 }
 
 void main()
 {
     Test myTest1;
     A topA;
 
     void _someFunc( ref const(A) myA )
     {
         myTest1 = Test(myA);
     }
 
     Test myTest2 = Test(topA);      // is ok as expected
     _someFunc( topA );		  // error.
 }
the question is: "why do you want `mA` to be const?" leaving aside compiler complaints this is the one and important question. let's try to fix your code instead of devising workarounds to beat the compiler. ;-) please remember that D `const` is not the same as C/C++ `const`, so my question is not so stupid as it may look. p.s. you can use `in A myA` instead of `ref const A myA`, in most cases compiler is intelligent enough to use "pass-by-reference" for `in` args.
Jan 02 2015
parent reply ted <foo bar.com> writes:
Well, I just cleared up some of my misunderstanding.

I did not realise the mA (within struct Test) would be a _copy_ of arg, not 
a reference (pointer) to arg. 

So the more correct code snippet would be:

struct A
{
    int someInt;
}

struct Test
{
     property { const(A) getA() { return *mA; } }
    this( in A arg )
    {
        mA = &arg;
    }

private:
    const A* mA;
}

void main()
{
    Test myTest1;
    A topA;

    topA.someInt = 100;

    void _someFunc( in A myA )
    {
        myTest1 = Test(myA);
    }

    Test myTest2 = Test(topA);      // is ok as expected
    _someFunc( topA );


}

which fails to compile for the same reason (test.d(30): Error: cannot 
modify struct myTest1 Test with immutable members)

(I have been able to work around my original issue by removing  safe from 
Test, and then cast()ing ... but this is ugly)

This code snippet is me trying to understand whether I have a fundamental 
misunderstanding with const, or whether it is an issue with the compiler

(I agree with your view that 'fixing my code' is the correct approach, btw 
- I'm still curious).

(The reason for this basic structure is that _someFunc is a callback from 
another module, and myTest1 contains state information that is 
created/adjusted via the callback. And (to be safe) myTest1 contains 
references to other structures that it should not adjust - hence the 
const()). 

....and I can't use  safe and pointers......

So, it appears I need to do what I want with classes, not structs:
i.e.

import std.stdio;

class A
{
    int someInt;
}

 safe class Test
{
     property { const(A) getA() { return mA; } }
    this( in A arg )
    {
        mA = arg;
    }

private:
    const A mA;
}

void main()
{
    Test myTest1;
    A topA = new A;

    topA.someInt = 100;

    void _someFunc( in A myA )
    {
        myTest1 = new Test(myA);
    }

    Test myTest2 = new Test(topA);      // is ok as expected
    _someFunc( topA );

    writeln( "topA: ", topA.someInt );
    writeln( "myTest1.A: ", myTest1.getA.someInt );
}

I'm happy I've got a workaround (which seems to safely preserve the 
const'dness), but I still don't understand why the compiler barfs on the 
struct version as it should be 'replacing' the higher scoped variable....

anyway...thanks for your response.....apologies for the streaming 
conciousness of this reply.......
Jan 02 2015
next sibling parent reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sat, 03 Jan 2015 14:45:24 +1030
ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 Well, I just cleared up some of my misunderstanding.
=20
 I did not realise the mA (within struct Test) would be a _copy_ of arg, n=
ot=20
 a reference (pointer) to arg.=20
=20
 So the more correct code snippet would be:
=20
 struct A
 {
     int someInt;
 }
=20
 struct Test
 {
      property { const(A) getA() { return *mA; } }
     this( in A arg )
     {
         mA =3D &arg;
     }
=20
 private:
     const A* mA;
 }
=20
 void main()
 {
     Test myTest1;
     A topA;
=20
     topA.someInt =3D 100;
=20
     void _someFunc( in A myA )
     {
         myTest1 =3D Test(myA);
     }
=20
     Test myTest2 =3D Test(topA);      // is ok as expected
     _someFunc( topA );
=20
=20
 }
nonononono! ;-) please, don't use pointers like this. try to avoid pointers altogether, they are VERY dangerous. as for your sample: it is already invalid. see: this (in A arg) { mA =3D &arg; } you are storing the address of *local* *var* *from* *stack* here. then local will go out of scope and... BANG! everyone is dead.
 This code snippet is me trying to understand whether I have a fundamental=
=20
 misunderstanding with const, or whether it is an issue with the compiler
seems that you didn't get D `const`. it's deffers from C/C++ `const`. in most cases you don't need to use it at all. the funny thing of D const is that const "variable" cannot be changed in any way after assigning. that's why compiler complains: you're trying to change variable with `const` part, which is forbidden. structure instance with const fields can be initialized only once, upon creation. so did `Test myTest1;` -- you initialized `myTest1` with default values. you can't reinitialize it later. in C++ constness on member doesn't impose such restrictions. this is confusing for newcomers with C/C++ expirience. the best advice here is "don't use `const` in D unless you are fully understand what you are doing and how it work in D".
 I'm happy I've got a workaround (which seems to safely preserve the=20
 const'dness), but I still don't understand why the compiler barfs on the=
=20
 struct version as it should be 'replacing' the higher scoped variable....
you don't need `const` here at all. as far as i can see you want to tell the compiler that `mA` must not be changed after creating a struct. to achieve that you'd better move your struct to separate module and provide read-only getter. you need to move your struct to separate module 'cause `private` modifier will not affect the code in the same module. one of the D rules is that the code in one module can access anything that was declared in the same module, including "private" members of structs/classes. C++ tries to achieve that with "friend" members, but as D has full-fledged module system, it doesn't need such tricks. tl;dr: don't use `const` here, provide an accessor and stop worrying/helping the compiler. ;-)
Jan 02 2015
next sibling parent reply ted <foo bar.com> writes:
ketmar via Digitalmars-d-learn wrote:

 On Sat, 03 Jan 2015 14:45:24 +1030
 ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:
 
 Well, I just cleared up some of my misunderstanding.
 
 I did not realise the mA (within struct Test) would be a _copy_ of arg,
 not a reference (pointer) to arg.
 
 So the more correct code snippet would be:
 
 struct A
 {
     int someInt;
 }
 
 struct Test
 {
      property { const(A) getA() { return *mA; } }
     this( in A arg )
     {
         mA = &arg;
     }
 
 private:
     const A* mA;
 }
 
 void main()
 {
     Test myTest1;
     A topA;
 
     topA.someInt = 100;
 
     void _someFunc( in A myA )
     {
         myTest1 = Test(myA);
     }
 
     Test myTest2 = Test(topA);      // is ok as expected
     _someFunc( topA );
 
 
 }
nonononono! ;-) please, don't use pointers like this. try to avoid pointers altogether, they are VERY dangerous. as for your sample: it is already invalid. see: this (in A arg) { mA = &arg; } you are storing the address of *local* *var* *from* *stack* here. then local will go out of scope and... BANG! everyone is dead.
Oops...true (*blush*) - should have noticed this.
 
 This code snippet is me trying to understand whether I have a
 fundamental misunderstanding with const, or whether it is an issue with
 the compiler
seems that you didn't get D `const`. it's deffers from C/C++ `const`. in most cases you don't need to use it at all. the funny thing of D const is that const "variable" cannot be changed in any way after assigning. that's why compiler complains: you're trying to change variable with `const` part, which is forbidden. structure instance with const fields can be initialized only once, upon creation. so did `Test myTest1;` -- you initialized `myTest1` with default values. you can't reinitialize it later. in C++ constness on member doesn't impose such restrictions. this is confusing for newcomers with C/C++ expirience. the best advice here is "don't use `const` in D unless you are fully understand what you are doing and how it work in D".
Ironically, I'm trying to use const in an effort to understand it...but there seems to be an unusual amount of pain until I grok it.
 
 I'm happy I've got a workaround (which seems to safely preserve the
 const'dness), but I still don't understand why the compiler barfs on the
 struct version as it should be 'replacing' the higher scoped
 variable....
you don't need `const` here at all. as far as i can see you want to tell the compiler that `mA` must not be changed after creating a struct. to achieve that you'd better move your struct to separate module and provide read-only getter. you need to move your struct to separate module 'cause `private` modifier will not affect the code in the same module. one of the D rules is that the code in one module can access anything that was declared in the same module, including "private" members of structs/classes. C++ tries to achieve that with "friend" members, but as D has full-fledged module system, it doesn't need such tricks. tl;dr: don't use `const` here, provide an accessor and stop worrying/helping the compiler. ;-)
Yeah.....its one of those cases where the prototype code is getting too ugly, and a refactorisation is required.....but no time !! many thanks, ted
Jan 02 2015
parent reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sat, 03 Jan 2015 15:56:58 +1030
ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 Ironically, I'm trying to use const in an effort to understand it...but=20
 there seems to be an unusual amount of pain until I grok it.
just remember that `const` "infects" everything down to the bytes when it's applied. and it's forbidden to overwrite `const` vars with different values. in your canse it "infects" your `A` struct, effectively converting it to `const A` (with `const int someInt`). and as you can't change "consted" value, and structs are `memcpy()`ed... compiler tracked that down and refused to do it. so you don't really need `const` fields in D. you can make getters `const` though, so that they can be used on `const MyStruct` instances. so two rules should help you here: 1. it's forbidden to overwrite `const` vars with new values. 2. `const` will infect everything down to bytes. maybe this will help you like it helps me. ;-)
Jan 02 2015
parent reply ted <foo bar.com> writes:
ketmar via Digitalmars-d-learn wrote:

 On Sat, 03 Jan 2015 15:56:58 +1030
 ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:
 
 Ironically, I'm trying to use const in an effort to understand it...but
 there seems to be an unusual amount of pain until I grok it.
just remember that `const` "infects" everything down to the bytes when it's applied. and it's forbidden to overwrite `const` vars with different values. in your canse it "infects" your `A` struct, effectively converting it to `const A` (with `const int someInt`). and as you can't change "consted" value, and structs are `memcpy()`ed... compiler tracked that down and refused to do it. so you don't really need `const` fields in D. you can make getters `const` though, so that they can be used on `const MyStruct` instances. so two rules should help you here: 1. it's forbidden to overwrite `const` vars with new values. 2. `const` will infect everything down to bytes. maybe this will help you like it helps me. ;-)
Thanks for your help....besides the issues that you pointed out, I had forgotten that structs are always copied (I'm so used to thinking in pointers (if you ignore the fubar from my earlier example) - and a world where copying data structures is an expensive exercise to be avoided)....... I'm now taking the view that const is there for the compiler to optimise code on the basis that nothing can alter it once set (and can only be set on initialisation). So I see your point that it would not be used very often in general defensive code - better to provide access restrictions (getters) for most cases. I am refactoring the code to use getters/setters.... One of the really good things about D is that it is relatively painless to refactor when you have to - much less boilerplate to have to move around ! thanks again, regards, ted
Jan 02 2015
next sibling parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sat, 03 Jan 2015 16:40:14 +1030
ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 I'm now taking the view that const is there for the compiler to optimise=
=20
 code on the basis that nothing can alter it once set (and can only be set=
=20
 on initialisation). So I see your point that it would not be used very=20
 often in general defensive code - better to provide access restrictions=20
 (getters) for most cases.
exactly! ;-)
 thanks again,
you're welcome. hope you will keep enjoying D, it's great. ;-)
Jan 02 2015
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/02/2015 10:10 PM, ted wrote:

 I'm now taking the view that const is there for the compiler to optimise
 code on the basis that nothing can alter it once set (and can only be set
 on initialisation).
Of course, that is true for const values, not for const references. In the latter case it means "I will not modify but others may modify", so the optimization that you mention does not apply to const in general. However, immutable can be used for optimization. Ali
Jan 03 2015
parent ted <foo bar.com> writes:
Ali Çehreli wrote:

 On 01/02/2015 10:10 PM, ted wrote:
 
  > I'm now taking the view that const is there for the compiler to
  > optimise code on the basis that nothing can alter it once set (and can
  > only be set on initialisation).
 
 Of course, that is true for const values, not for const references. In
 the latter case it means "I will not modify but others may modify", so
 the optimization that you mention does not apply to const in general.
 However, immutable can be used for optimization.
 
 Ali
....thats (sort of) where I had started from..... My (clearly incorrect) interpretation was that reinitialising 'myTest1' was part of the 'others may modify' category because of the fact that it was in scope of the _someFunc function.... ...anyway....lots to learn. BTW: your book is excellent, and as soon as you have it available for purchase I will certainly be buying a copy.
Jan 03 2015
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/02/2015 09:07 PM, ketmar via Digitalmars-d-learn wrote:

 structure instance with const fields can be initialized only once, upon
 creation. so did `Test myTest1;` -- you initialized `myTest1` with
 default values. you can't reinitialize it later.

 in C++ constness on member doesn't impose such restrictions.
C++ has the same restriction: const members make objects unassignable: struct S { const int i; S() : i() {} }; int main() { S a; S b; a = b; } error: non-static const member ‘const int S::i’, can't use default assignment operator Ali
Jan 03 2015
parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sat, 03 Jan 2015 01:00:58 -0800
Ali =C3=87ehreli via Digitalmars-d-learn <digitalmars-d-learn puremagic.com>
wrote:

 On 01/02/2015 09:07 PM, ketmar via Digitalmars-d-learn wrote:
=20
  > structure instance with const fields can be initialized only once, upon
  > creation. so did `Test myTest1;` -- you initialized `myTest1` with
  > default values. you can't reinitialize it later.
  >
  > in C++ constness on member doesn't impose such restrictions.
=20
 C++ has the same restriction: const members make objects unassignable:
ah, thanks. it's a joy to forgetting C++ to this extent. ;-)
Jan 03 2015
prev sibling parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sat, 03 Jan 2015 14:45:24 +1030
ted via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

p.s. also please note that structs in D are always passed by value and
copied (until you not explicitly ask for something another). so:

  MyStruct a;
  MyStruct b;
  b =3D a;

actually does `memcpy()` (with postblit if there is any). and it's
forbidden to overwrite `const` variable contents. that's why compiler
complains: by assigning to `mA` you are trying to overwrite contents of
`const` variable.
Jan 02 2015