www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Immutable member functions and private members

reply simendsjo <simendsjo gmail.com> writes:
I have a struct with a private member that is only ever accessed through 
a single property method - even from within the struct.
As this property fills the value on the first access, it cannot be 
immutable, and as such, none of the many methods accessing this property 
can be immutable methods.

This is according to specification, but I thought that since the single 
write to the property is done at one, and only one, access point, that 
it would be safe?

I could fill this value in the constructor, but it's a bit slow, so I'd 
rather do it only if needed.

And is there any potential performance optimizations done by the 
compiler, or is it "only" for safety?
Is there a way to hack around this, and more importantly, is it safe to 
do so, or will I open Pandora's box?


Small example:

int len(const char[] c) {
     return c.length;
}

struct S {
     private immutable(char)[] _v;
      property immutable(char[]) v() { // Cannot be immutable method
         if(!_v)
             _v = "init"; /* or from external function */
         return _v;
     }

      property int a() { // and so this cannot be immutable method
         return len(v); /* notice the property function v that might 
modify _v */
     }
}

void main() {
     S s;
     s.a;
}
Aug 03 2011
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 03 August 2011 10:37:58 simendsjo wrote:
 I have a struct with a private member that is only ever accessed through
 a single property method - even from within the struct.
 As this property fills the value on the first access, it cannot be
 immutable, and as such, none of the many methods accessing this property
 can be immutable methods.
 
 This is according to specification, but I thought that since the single
 write to the property is done at one, and only one, access point, that
 it would be safe?
 
 I could fill this value in the constructor, but it's a bit slow, so I'd
 rather do it only if needed.
 
 And is there any potential performance optimizations done by the
 compiler, or is it "only" for safety?
 Is there a way to hack around this, and more importantly, is it safe to
 do so, or will I open Pandora's box?
 
 
 Small example:
 
 int len(const char[] c) {
      return c.length;
 }
 
 struct S {
      private immutable(char)[] _v;
       property immutable(char[]) v() { // Cannot be immutable method
          if(!_v)
              _v = "init"; /* or from external function */
          return _v;
      }
 
       property int a() { // and so this cannot be immutable method
          return len(v); /* notice the property function v that might
 modify _v */
      }
 }
 
 void main() {
      S s;
      s.a;
 }
You're basically looking for logical const - albeit a subset which would be much easier to implement were we to implement it (that is, a lazy initialized const or immutable member variable). D has no support for logical const. Even worse, you're looking for logical immutable (which makes no sense at all beyond perhaps lazy initialization and probably doesn't even make sense there). The thing is that immutable methods are pointless unless you make the struct immutable (if you want to be able to call them with both a mutable and immutable instance of the struct, then you need the functions to be const, not immutable). And if you make the struct immutable, the compiler is free to put it in read-only memory if it so chooses, at which point setting _anything_ in the struct after the constructor has run is likely to blow up. So, even if you can get around the issue via casts and get both lazy initialization and immutable methods, there's a good chance that it'll blow up at some point (as in segfault or worse). If you were trying to do this with const, you might get away with it (though you'd be stepping outside of the type lsystem by casting away const and then altering anything - it's undefined behavior). But with immutable, there's no way that this is a good idea. Lazy initialization with const or immutable member variables just is _not_ a good idea in D. D provides no type-safe way to do this. You must break the type system by casting away const or immutable to even attempt it. Convievably, in the case of const, the language could be extended to allow for lazy initialization of member variables, but there's no way that it could do that with immutable (because the variable could conceivably be put in read- only memory), and even if it were done, it would likely have to be a D3 feature. Syntactically, it would probably be something like this: lazy int v = initFunc(); and then when v was first accessed, initFunc would be called and v set to that value. But that could be ugly and inefficient to implement even if it's theoretically possible, so I wouldn't bet on anything like that making it into the language. Regardless, it wouldn't be until D3. For now, D doesn't support any kind of logical const. http://stackoverflow.com/questions/4219600/logical-const-in-d - Jonathan M Davis
Aug 03 2011
parent reply simendsjo <simendsjo gmail.com> writes:
On 03.08.2011 10:52, Jonathan M Davis wrote:
 On Wednesday 03 August 2011 10:37:58 simendsjo wrote:
 I have a struct with a private member that is only ever accessed through
 a single property method - even from within the struct.
 As this property fills the value on the first access, it cannot be
 immutable, and as such, none of the many methods accessing this property
 can be immutable methods.

 This is according to specification, but I thought that since the single
 write to the property is done at one, and only one, access point, that
 it would be safe?

 I could fill this value in the constructor, but it's a bit slow, so I'd
 rather do it only if needed.

 And is there any potential performance optimizations done by the
 compiler, or is it "only" for safety?
 Is there a way to hack around this, and more importantly, is it safe to
 do so, or will I open Pandora's box?


 Small example:

 int len(const char[] c) {
       return c.length;
 }

 struct S {
       private immutable(char)[] _v;
        property immutable(char[]) v() { // Cannot be immutable method
           if(!_v)
               _v = "init"; /* or from external function */
           return _v;
       }

        property int a() { // and so this cannot be immutable method
           return len(v); /* notice the property function v that might
 modify _v */
       }
 }

 void main() {
       S s;
       s.a;
 }
You're basically looking for logical const - albeit a subset which would be much easier to implement were we to implement it (that is, a lazy initialized const or immutable member variable). D has no support for logical const. Even worse, you're looking for logical immutable (which makes no sense at all beyond perhaps lazy initialization and probably doesn't even make sense there). The thing is that immutable methods are pointless unless you make the struct immutable (if you want to be able to call them with both a mutable and immutable instance of the struct, then you need the functions to be const, not immutable). And if you make the struct immutable, the compiler is free to put it in read-only memory if it so chooses, at which point setting _anything_ in the struct after the constructor has run is likely to blow up. So, even if you can get around the issue via casts and get both lazy initialization and immutable methods, there's a good chance that it'll blow up at some point (as in segfault or worse). If you were trying to do this with const, you might get away with it (though you'd be stepping outside of the type lsystem by casting away const and then altering anything - it's undefined behavior). But with immutable, there's no way that this is a good idea. Lazy initialization with const or immutable member variables just is _not_ a good idea in D. D provides no type-safe way to do this. You must break the type system by casting away const or immutable to even attempt it. Convievably, in the case of const, the language could be extended to allow for lazy initialization of member variables, but there's no way that it could do that with immutable (because the variable could conceivably be put in read- only memory), and even if it were done, it would likely have to be a D3 feature. Syntactically, it would probably be something like this: lazy int v = initFunc(); and then when v was first accessed, initFunc would be called and v set to that value. But that could be ugly and inefficient to implement even if it's theoretically possible, so I wouldn't bet on anything like that making it into the language. Regardless, it wouldn't be until D3. For now, D doesn't support any kind of logical const. http://stackoverflow.com/questions/4219600/logical-const-in-d - Jonathan M Davis
Thanks! I'm not really sure the compiler could put my struct in ROM. My lazy parameter is immutable(char)[], so the compiler should see that I have a non-immutable reference. The entire struct is immutable without this lazy variable though. It only has two handles for passing to external functions. I really would like to always use it only as immutable s = S(123). It makes no sense for it to be mutable at all. Below is an exact example of what I want to do. If I move the handle2 calculation to the ctor and use const methods, I still cannot call the methods using a const variable though.. Bug? const s = S(100); s.a; // function t.S.a () immutable is not callable using argument type It says immutable when it should say const..? immutable s = S(100); s.a; // works on both const and immutable ---- import std.conv, std.exception; // external expensive function extern(System) char[] getHandle2(const int handle) { return to!(char[])(handle); } // other external functions taking string handle instead of int extern(System) int extFunc1(string handle2) { return to!int(handle2); } struct S { immutable int handle; private immutable(char)[] _handle2; this(int handle) { this.handle = handle; } property immutable(char[]) handle2() { if(!_handle2) { auto buf = getHandle2(handle); _handle2 = assumeUnique(buf); } return _handle2; } property int a() { return extFunc1(handle2); } // many more properties like this } void main() { auto s = S(100); assert(s.a == 100); immutable s2 = S(100); //assert(s2.a == 100); // oops, a not immutable }
Aug 03 2011
next sibling parent reply simendsjo <simendsjo gmail.com> writes:
On 03.08.2011 11:44, simendsjo wrote:
 On 03.08.2011 10:52, Jonathan M Davis wrote:
 On Wednesday 03 August 2011 10:37:58 simendsjo wrote:
 I have a struct with a private member that is only ever accessed through
 a single property method - even from within the struct.
 As this property fills the value on the first access, it cannot be
 immutable, and as such, none of the many methods accessing this property
 can be immutable methods.

 This is according to specification, but I thought that since the single
 write to the property is done at one, and only one, access point, that
 it would be safe?

 I could fill this value in the constructor, but it's a bit slow, so I'd
 rather do it only if needed.

 And is there any potential performance optimizations done by the
 compiler, or is it "only" for safety?
 Is there a way to hack around this, and more importantly, is it safe to
 do so, or will I open Pandora's box?


 Small example:

 int len(const char[] c) {
 return c.length;
 }

 struct S {
 private immutable(char)[] _v;
  property immutable(char[]) v() { // Cannot be immutable method
 if(!_v)
 _v = "init"; /* or from external function */
 return _v;
 }

  property int a() { // and so this cannot be immutable method
 return len(v); /* notice the property function v that might
 modify _v */
 }
 }

 void main() {
 S s;
 s.a;
 }
You're basically looking for logical const - albeit a subset which would be much easier to implement were we to implement it (that is, a lazy initialized const or immutable member variable). D has no support for logical const. Even worse, you're looking for logical immutable (which makes no sense at all beyond perhaps lazy initialization and probably doesn't even make sense there). The thing is that immutable methods are pointless unless you make the struct immutable (if you want to be able to call them with both a mutable and immutable instance of the struct, then you need the functions to be const, not immutable). And if you make the struct immutable, the compiler is free to put it in read-only memory if it so chooses, at which point setting _anything_ in the struct after the constructor has run is likely to blow up. So, even if you can get around the issue via casts and get both lazy initialization and immutable methods, there's a good chance that it'll blow up at some point (as in segfault or worse). If you were trying to do this with const, you might get away with it (though you'd be stepping outside of the type lsystem by casting away const and then altering anything - it's undefined behavior). But with immutable, there's no way that this is a good idea. Lazy initialization with const or immutable member variables just is _not_ a good idea in D. D provides no type-safe way to do this. You must break the type system by casting away const or immutable to even attempt it. Convievably, in the case of const, the language could be extended to allow for lazy initialization of member variables, but there's no way that it could do that with immutable (because the variable could conceivably be put in read- only memory), and even if it were done, it would likely have to be a D3 feature. Syntactically, it would probably be something like this: lazy int v = initFunc(); and then when v was first accessed, initFunc would be called and v set to that value. But that could be ugly and inefficient to implement even if it's theoretically possible, so I wouldn't bet on anything like that making it into the language. Regardless, it wouldn't be until D3. For now, D doesn't support any kind of logical const. http://stackoverflow.com/questions/4219600/logical-const-in-d - Jonathan M Davis
Thanks! I'm not really sure the compiler could put my struct in ROM. My lazy parameter is immutable(char)[], so the compiler should see that I have a non-immutable reference. The entire struct is immutable without this lazy variable though. It only has two handles for passing to external functions. I really would like to always use it only as immutable s = S(123). It makes no sense for it to be mutable at all. Below is an exact example of what I want to do. If I move the handle2 calculation to the ctor and use const methods, I still cannot call the methods using a const variable though.. Bug? const s = S(100); s.a; // function t.S.a () immutable is not callable using argument type It says immutable when it should say const..? immutable s = S(100); s.a; // works on both const and immutable ---- import std.conv, std.exception; // external expensive function extern(System) char[] getHandle2(const int handle) { return to!(char[])(handle); } // other external functions taking string handle instead of int extern(System) int extFunc1(string handle2) { return to!int(handle2); } struct S { immutable int handle; private immutable(char)[] _handle2; this(int handle) { this.handle = handle; } property immutable(char[]) handle2() { if(!_handle2) { auto buf = getHandle2(handle); _handle2 = assumeUnique(buf); } return _handle2; } property int a() { return extFunc1(handle2); } // many more properties like this } void main() { auto s = S(100); assert(s.a == 100); immutable s2 = S(100); //assert(s2.a == 100); // oops, a not immutable }
Oh, and BCS example didn't work directly. I had to do this: struct S { private immutable(char)[] _handle2; property immutable(char[]) handle2() immutable { auto s = cast(S)this; if(!_handle2) { s._handle2 = "some value"; } return s._handle2; // just _handle2 is empty, so I have to use s._handle2 } } Given that I have the non-immutable reference. Would this hack be safe?
Aug 03 2011
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 03 August 2011 12:00:18 simendsjo wrote:
 On 03.08.2011 11:44, simendsjo wrote:
 On 03.08.2011 10:52, Jonathan M Davis wrote:
 On Wednesday 03 August 2011 10:37:58 simendsjo wrote:
 I have a struct with a private member that is only ever accessed
 through a single property method - even from within the struct.
 As this property fills the value on the first access, it cannot be
 immutable, and as such, none of the many methods accessing this
 property can be immutable methods.
 
 This is according to specification, but I thought that since the
 single
 write to the property is done at one, and only one, access point,
 that
 it would be safe?
 
 I could fill this value in the constructor, but it's a bit slow, so
 I'd
 rather do it only if needed.
 
 And is there any potential performance optimizations done by the
 compiler, or is it "only" for safety?
 Is there a way to hack around this, and more importantly, is it safe
 to
 do so, or will I open Pandora's box?
 
 
 Small example:
 
 int len(const char[] c) {
 return c.length;
 }
 
 struct S {
 private immutable(char)[] _v;
  property immutable(char[]) v() { // Cannot be immutable method
 if(!_v)
 _v = "init"; /* or from external function */
 return _v;
 }
 
  property int a() { // and so this cannot be immutable method
 return len(v); /* notice the property function v that might
 modify _v */
 }
 }
 
 void main() {
 S s;
 s.a;
 }
You're basically looking for logical const - albeit a subset which would be much easier to implement were we to implement it (that is, a lazy initialized const or immutable member variable). D has no support for logical const. Even worse, you're looking for logical immutable (which makes no sense at all beyond perhaps lazy initialization and probably doesn't even make sense there). The thing is that immutable methods are pointless unless you make the struct immutable (if you want to be able to call them with both a mutable and immutable instance of the struct, then you need the functions to be const, not immutable). And if you make the struct immutable, the compiler is free to put it in read-only memory if it so chooses, at which point setting _anything_ in the struct after the constructor has run is likely to blow up. So, even if you can get around the issue via casts and get both lazy initialization and immutable methods, there's a good chance that it'll blow up at some point (as in segfault or worse). If you were trying to do this with const, you might get away with it (though you'd be stepping outside of the type lsystem by casting away const and then altering anything - it's undefined behavior). But with immutable, there's no way that this is a good idea. Lazy initialization with const or immutable member variables just is _not_ a good idea in D. D provides no type-safe way to do this. You must break the type system by casting away const or immutable to even attempt it. Convievably, in the case of const, the language could be extended to allow for lazy initialization of member variables, but there's no way that it could do that with immutable (because the variable could conceivably be put in read- only memory), and even if it were done, it would likely have to be a D3 feature. Syntactically, it would probably be something like this: lazy int v = initFunc(); and then when v was first accessed, initFunc would be called and v set to that value. But that could be ugly and inefficient to implement even if it's theoretically possible, so I wouldn't bet on anything like that making it into the language. Regardless, it wouldn't be until D3. For now, D doesn't support any kind of logical const. http://stackoverflow.com/questions/4219600/logical-const-in-d - Jonathan M Davis
Thanks! I'm not really sure the compiler could put my struct in ROM. My lazy parameter is immutable(char)[], so the compiler should see that I have a non-immutable reference. The entire struct is immutable without this lazy variable though. It only has two handles for passing to external functions. I really would like to always use it only as immutable s = S(123). It makes no sense for it to be mutable at all. Below is an exact example of what I want to do. If I move the handle2 calculation to the ctor and use const methods, I still cannot call the methods using a const variable though.. Bug? const s = S(100); s.a; // function t.S.a () immutable is not callable using argument type It says immutable when it should say const..? immutable s = S(100); s.a; // works on both const and immutable ---- import std.conv, std.exception; // external expensive function extern(System) char[] getHandle2(const int handle) { return to!(char[])(handle); } // other external functions taking string handle instead of int extern(System) int extFunc1(string handle2) { return to!int(handle2); } struct S { immutable int handle; private immutable(char)[] _handle2; this(int handle) { this.handle = handle; } property immutable(char[]) handle2() { if(!_handle2) { auto buf = getHandle2(handle); _handle2 = assumeUnique(buf); } return _handle2; } property int a() { return extFunc1(handle2); } // many more properties like this } void main() { auto s = S(100); assert(s.a == 100); immutable s2 = S(100); //assert(s2.a == 100); // oops, a not immutable }
Oh, and BCS example didn't work directly. I had to do this: struct S { private immutable(char)[] _handle2; property immutable(char[]) handle2() immutable { auto s = cast(S)this; if(!_handle2) { s._handle2 = "some value"; } return s._handle2; // just _handle2 is empty, so I have to use s._handle2 } } Given that I have the non-immutable reference. Would this hack be safe?
It's unsafe to you ever alter a variable by casting away its constness or immutability first. It might work. It might not. With const, it'll probably work, but it's undefined, so there's no guarantee that it'll continue to work. With immutable, it's much more iffy. An immutable variable is shared, and the compiler is free to do stuff like put it in read-only memory. If it's just a variable on the stack, it might be okay. But you've thrown away all safety as soon as you cast away const or immutable. And there's every chance that it'll screw with optimizations - particularly those relating to pure. As all you're doing is lazy initialization, that particular problem is less likely (since each call to the function results in the same result), but there are no guarantees. Bottom line: It's never safe to cast away const or immutable on a variable and then alter it. It'll work some of the time, but the behavior is undefined. - Jonathan M Davis
Aug 03 2011
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 03 August 2011 11:44:27 simendsjo wrote:
 On 03.08.2011 10:52, Jonathan M Davis wrote:
 On Wednesday 03 August 2011 10:37:58 simendsjo wrote:
 I have a struct with a private member that is only ever accessed
 through
 a single property method - even from within the struct.
 As this property fills the value on the first access, it cannot be
 immutable, and as such, none of the many methods accessing this
 property
 can be immutable methods.
 
 This is according to specification, but I thought that since the
 single
 write to the property is done at one, and only one, access point, that
 it would be safe?
 
 I could fill this value in the constructor, but it's a bit slow, so
 I'd
 rather do it only if needed.
 
 And is there any potential performance optimizations done by the
 compiler, or is it "only" for safety?
 Is there a way to hack around this, and more importantly, is it safe
 to
 do so, or will I open Pandora's box?
 
 
 Small example:
 
 int len(const char[] c) {
 
       return c.length;
 
 }
 
 struct S {
 
       private immutable(char)[] _v;
        property immutable(char[]) v() { // Cannot be immutable
       method
       
           if(!_v)
           
               _v = "init"; /* or from external function
               */
           
           return _v;
       
       }
       
        property int a() { // and so this cannot be immutable
       method
       
           return len(v); /* notice the property function v
           that might
 
 modify _v */
 
       }
 
 }
 
 void main() {
 
       S s;
       s.a;
 
 }
You're basically looking for logical const - albeit a subset which would be much easier to implement were we to implement it (that is, a lazy initialized const or immutable member variable). D has no support for logical const. Even worse, you're looking for logical immutable (which makes no sense at all beyond perhaps lazy initialization and probably doesn't even make sense there). The thing is that immutable methods are pointless unless you make the struct immutable (if you want to be able to call them with both a mutable and immutable instance of the struct, then you need the functions to be const, not immutable). And if you make the struct immutable, the compiler is free to put it in read-only memory if it so chooses, at which point setting _anything_ in the struct after the constructor has run is likely to blow up. So, even if you can get around the issue via casts and get both lazy initialization and immutable methods, there's a good chance that it'll blow up at some point (as in segfault or worse). If you were trying to do this with const, you might get away with it (though you'd be stepping outside of the type lsystem by casting away const and then altering anything - it's undefined behavior). But with immutable, there's no way that this is a good idea. Lazy initialization with const or immutable member variables just is _not_ a good idea in D. D provides no type-safe way to do this. You must break the type system by casting away const or immutable to even attempt it. Convievably, in the case of const, the language could be extended to allow for lazy initialization of member variables, but there's no way that it could do that with immutable (because the variable could conceivably be put in read- only memory), and even if it were done, it would likely have to be a D3 feature. Syntactically, it would probably be something like this: lazy int v = initFunc(); and then when v was first accessed, initFunc would be called and v set to that value. But that could be ugly and inefficient to implement even if it's theoretically possible, so I wouldn't bet on anything like that making it into the language. Regardless, it wouldn't be until D3. For now, D doesn't support any kind of logical const. http://stackoverflow.com/questions/4219600/logical-const-in-d - Jonathan M Davis
Thanks! I'm not really sure the compiler could put my struct in ROM. My lazy parameter is immutable(char)[], so the compiler should see that I have a non-immutable reference. The entire struct is immutable without this lazy variable though. It only has two handles for passing to external functions. I really would like to always use it only as immutable s = S(123). It makes no sense for it to be mutable at all. Below is an exact example of what I want to do. If I move the handle2 calculation to the ctor and use const methods, I still cannot call the methods using a const variable though.. Bug? const s = S(100); s.a; // function t.S.a () immutable is not callable using argument type It says immutable when it should say const..? immutable s = S(100); s.a; // works on both const and immutable
If a variable is const, you should only be able to call const functions on it. If it's immutable, you can call either const or immutable functions. If it's mutable, then you can call either const or non-const, non-immutable functions. If it's complaining about being unable to call a function on an immutable variable when the variable is const, then it's a bug.
 ----
 
 import std.conv, std.exception;
 
 // external expensive function
 extern(System) char[] getHandle2(const int handle) {
      return to!(char[])(handle);
 }
 
 // other external functions taking string handle instead of int
 extern(System) int extFunc1(string handle2) {
      return to!int(handle2);
 }
 
 struct S {
      immutable int handle;
      private immutable(char)[] _handle2;
 
      this(int handle) {
          this.handle = handle;
      }
 
       property immutable(char[]) handle2() {
          if(!_handle2) {
              auto buf = getHandle2(handle);
              _handle2 = assumeUnique(buf);
          }
          return _handle2;
      }
 
       property int a() {
          return extFunc1(handle2);
      }
 
      // many more properties like this
 }
 
 void main() {
      auto s = S(100);
      assert(s.a == 100);
 
      immutable s2 = S(100);
      //assert(s2.a == 100); // oops, a not immutable
 }
You can't call a non-const, non-immutable function on an immutable variable. a must either be const or immutable to be callable here. In the general case, I'd advise against trying to have member variables of structs which are either const or immutable, because then you can't assign to them, which means that you can't use them in arrays and the like - only as local variables which are directly initialized. As long as the struct is capable of being immutable, you can then have immutable variables of that type if you want to, but you can then also stick it in arrays and the like if need be. But until http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed, dealing with const structs which need a postblit doesn't work, so depending on what you're doing using const or immutable with structs won't necessarily work. It'll usually work, but you could run into trouble if a struct has any kind of indirection in it. In any case, you pretty much either have to completely initialize a struct in its constructor or you can't have its member functions be const or immutable, and there are definite issues with having const or immutable structs (both in terms of bugs in the current implementation and because anything using init is stuck with the init value). So, I'd be careful about trying to force a struct to always be const or immutable. It's generally doable, but there can be negative consequences to it. - Jonathan M Davis
Aug 03 2011
parent simendsjo <simendsjo gmail.com> writes:
On 03.08.2011 12:01, Jonathan M Davis wrote:
 On Wednesday 03 August 2011 11:44:27 simendsjo wrote:
 On 03.08.2011 10:52, Jonathan M Davis wrote:
 On Wednesday 03 August 2011 10:37:58 simendsjo wrote:
 I have a struct with a private member that is only ever accessed
 through
 a single property method - even from within the struct.
 As this property fills the value on the first access, it cannot be
 immutable, and as such, none of the many methods accessing this
 property
 can be immutable methods.

 This is according to specification, but I thought that since the
 single
 write to the property is done at one, and only one, access point, that
 it would be safe?

 I could fill this value in the constructor, but it's a bit slow, so
 I'd
 rather do it only if needed.

 And is there any potential performance optimizations done by the
 compiler, or is it "only" for safety?
 Is there a way to hack around this, and more importantly, is it safe
 to
 do so, or will I open Pandora's box?


 Small example:

 int len(const char[] c) {

        return c.length;

 }

 struct S {

        private immutable(char)[] _v;
         property immutable(char[]) v() { // Cannot be immutable
        method

            if(!_v)

                _v = "init"; /* or from external function
                */

            return _v;

        }

         property int a() { // and so this cannot be immutable
        method

            return len(v); /* notice the property function v
            that might

 modify _v */

        }

 }

 void main() {

        S s;
        s.a;

 }
You're basically looking for logical const - albeit a subset which would be much easier to implement were we to implement it (that is, a lazy initialized const or immutable member variable). D has no support for logical const. Even worse, you're looking for logical immutable (which makes no sense at all beyond perhaps lazy initialization and probably doesn't even make sense there). The thing is that immutable methods are pointless unless you make the struct immutable (if you want to be able to call them with both a mutable and immutable instance of the struct, then you need the functions to be const, not immutable). And if you make the struct immutable, the compiler is free to put it in read-only memory if it so chooses, at which point setting _anything_ in the struct after the constructor has run is likely to blow up. So, even if you can get around the issue via casts and get both lazy initialization and immutable methods, there's a good chance that it'll blow up at some point (as in segfault or worse). If you were trying to do this with const, you might get away with it (though you'd be stepping outside of the type lsystem by casting away const and then altering anything - it's undefined behavior). But with immutable, there's no way that this is a good idea. Lazy initialization with const or immutable member variables just is _not_ a good idea in D. D provides no type-safe way to do this. You must break the type system by casting away const or immutable to even attempt it. Convievably, in the case of const, the language could be extended to allow for lazy initialization of member variables, but there's no way that it could do that with immutable (because the variable could conceivably be put in read- only memory), and even if it were done, it would likely have to be a D3 feature. Syntactically, it would probably be something like this: lazy int v = initFunc(); and then when v was first accessed, initFunc would be called and v set to that value. But that could be ugly and inefficient to implement even if it's theoretically possible, so I wouldn't bet on anything like that making it into the language. Regardless, it wouldn't be until D3. For now, D doesn't support any kind of logical const. http://stackoverflow.com/questions/4219600/logical-const-in-d - Jonathan M Davis
Thanks! I'm not really sure the compiler could put my struct in ROM. My lazy parameter is immutable(char)[], so the compiler should see that I have a non-immutable reference. The entire struct is immutable without this lazy variable though. It only has two handles for passing to external functions. I really would like to always use it only as immutable s = S(123). It makes no sense for it to be mutable at all. Below is an exact example of what I want to do. If I move the handle2 calculation to the ctor and use const methods, I still cannot call the methods using a const variable though.. Bug? const s = S(100); s.a; // function t.S.a () immutable is not callable using argument type It says immutable when it should say const..? immutable s = S(100); s.a; // works on both const and immutable
If a variable is const, you should only be able to call const functions on it. If it's immutable, you can call either const or immutable functions. If it's mutable, then you can call either const or non-const, non-immutable functions. If it's complaining about being unable to call a function on an immutable variable when the variable is const, then it's a bug.
 ----

 import std.conv, std.exception;

 // external expensive function
 extern(System) char[] getHandle2(const int handle) {
       return to!(char[])(handle);
 }

 // other external functions taking string handle instead of int
 extern(System) int extFunc1(string handle2) {
       return to!int(handle2);
 }

 struct S {
       immutable int handle;
       private immutable(char)[] _handle2;

       this(int handle) {
           this.handle = handle;
       }

        property immutable(char[]) handle2() {
           if(!_handle2) {
               auto buf = getHandle2(handle);
               _handle2 = assumeUnique(buf);
           }
           return _handle2;
       }

        property int a() {
           return extFunc1(handle2);
       }

       // many more properties like this
 }

 void main() {
       auto s = S(100);
       assert(s.a == 100);

       immutable s2 = S(100);
       //assert(s2.a == 100); // oops, a not immutable
 }
You can't call a non-const, non-immutable function on an immutable variable. a must either be const or immutable to be callable here. In the general case, I'd advise against trying to have member variables of structs which are either const or immutable, because then you can't assign to them, which means that you can't use them in arrays and the like - only as local variables which are directly initialized. As long as the struct is capable of being immutable, you can then have immutable variables of that type if you want to, but you can then also stick it in arrays and the like if need be. But until http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed, dealing with const structs which need a postblit doesn't work, so depending on what you're doing using const or immutable with structs won't necessarily work. It'll usually work, but you could run into trouble if a struct has any kind of indirection in it. In any case, you pretty much either have to completely initialize a struct in its constructor or you can't have its member functions be const or immutable, and there are definite issues with having const or immutable structs (both in terms of bugs in the current implementation and because anything using init is stuck with the init value). So, I'd be careful about trying to force a struct to always be const or immutable. It's generally doable, but there can be negative consequences to it. - Jonathan M Davis
Thanks for your detailed answers. I'll stay away from trying to force it into immutable by casting :) It doesn't give away any mutable references anyway, so it's immutable in it's use even if the compiler doesn't know that.
Aug 03 2011