digitalmars.D.learn - Making array elements truly const/immutable
- Joseph Rushton Wakeling (36/36) Aug 02 2012 Hello all,
- Era Scarecrow (14/33) Aug 02 2012 If someone wants to modify something, they can go into assembly
- Joseph Rushton Wakeling (9/16) Aug 02 2012 That's a very good point. I'm not asking for something rock-solid, but ...
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (18/34) Aug 02 2012 .dup and .idup will always make a copy, std.conv.to won't:
- Joseph Rushton Wakeling (12/20) Aug 02 2012 Brilliant. You could actually say that my _real_ problem was how to cop...
- Adam D. Ruppe (9/9) Aug 02 2012 If a function is marked @safe, casting away immutable is
- Era Scarecrow (12/20) Aug 02 2012 to!()() should make a copy from immutable to non, and vice-verse.
Hello all, While playing around I noticed something that rather disturbed me about const/immutable's relationship with arrays. If I do e.g. import std.stdio; void main() { immutable(int)[] foo = [1, 2, 3, 4, 5]; int[] bar = cast(int[]) foo; bar[2] *= 2; foreach(b; bar) writeln(b); writeln(); foreach(f; foo) writeln(f); } ... then the tweak to bar will also affect foo. Now, it seems fairly evident why this is -- I'm taking something that is effectively a pointer to (immutable int) and casting it to a pointer to int, and so of course the latter can mutate the data contained. I can even see potential applications for this kind of cast. But, 2 questions: (i) is there a way to mark an array as "under no circumstances allow anything to modify the contents" in a way that _can't_ be cast away like this? and (ii) given that I can use instead bar = foo.dup to copy foo, is this guaranteed to produce a _copy_ or is it smart inasmuch as the compiler will check if bar is actually mutated and so only create a duplicate if needed? The motivation is that I want to have a class which contains a dynamic array as a private member variable, but which is readable through a class property. i.e. something like class Foo { private int[] _foo; property immutable(int)[] foo { return _foo; } } ... but where it's guaranteed that no external agent can touch the inside of _foo. So, yes, I could return _foo.idup, but I'd like to be able to guarantee that extra arrays won't be allocated unless strictly necessary.
Aug 02 2012
On Thursday, 2 August 2012 at 08:56:42 UTC, Joseph Rushton Wakeling wrote:But, 2 questions: (I) is there a way to Mark an array as "under no circumstances allow anything to modify the contents" in a way that _can't_ be cast away like this? and (ii) given that I can use instead bar = foo.dup to copy foo, is this guaranteed to produce a _copy_ or is it smart inasmuch as the compiler will check if bar is actually mutated and so only create a duplicate if needed?If someone wants to modify something, they can go into assembly language and force it or use C wrappers that lie wouldn't stop it. Comes down to the programmers in the end.The motivation is that I want to have a class which contains a dynamic array as a private member variable, but which is readable through a class property. I.e. something like class Foo { private int[] _foo; property immutable(int)[] foo { return _foo; } } ... but where it's guaranteed that no external agent can touch the inside of _foo. So, yes, I could return _foo.idup, but I'd like to be able to guarantee that extra arrays won't be allocated unless strictly necessary.I would thing the returned property would be dup'ed (since immutable cannot be const, since const says 'I won't change' and immutable is 'I can't change') and that problem goes away. If you returned const(int)[], then it may return the array pointer but the calling function (and others) shouldn't be able to modify it. It almost sounds like you'd want const access with COW-like properties (copy-on-write), so if you try to make changes it duplicates it and uses the duplicate afterwards. I began a test for COW arrays before, but haven't finished it.
Aug 02 2012
On 02/08/12 10:22, Era Scarecrow wrote:If someone wants to modify something, they can go into assembly language and force it or use C wrappers that lie wouldn't stop it. Comes down to the programmers in the end.That's a very good point. I'm not asking for something rock-solid, but just whether there's something I can declare that the compiler will pick up on in the usual case. Though actually, I'm warming to the possibility to un-const an array via a cast; it would be something to use very cautiously, but potentially useful in places.It almost sounds like you'd want const access with COW-like properties (copy-on-write), so if you try to make changes it duplicates it and uses the duplicate afterwards. I began a test for COW arrays before, but haven't finished it.Pretty much; but I think Ali's notes on std.conv.to have probably provided me with the real toolset I need. Thanks very much for the advice!
Aug 02 2012
On 08/02/2012 01:55 AM, Joseph Rushton Wakeling wrote:(i) is there a way to mark an array as "under no circumstances allow anything to modify the contents" in a way that _can't_ be cast away like this? and (ii) given that I can use instead bar = foo.dup to copy foo, is this guaranteed to produce a _copy_ or is it smart inasmuch as the compiler will check if bar is actually mutated and so only create a duplicate if needed?.dup and .idup will always make a copy, std.conv.to won't: import std.conv; void main() { immutable(int)[] i = [ 1, 2 ]; immutable(int)[] j = to!(immutable(int)[])(i); assert(i is j); }The motivation is that I want to have a class which contains a dynamic array as a private member variable, but which is readable through a class property. i.e. something like class Foo { private int[] _foo; property immutable(int)[] foo { return _foo; } }'const' is a better choice there: property const(int)[] foo() { return _foo; } You don't want to give the misleading promise that the data is immutable, as the caller may treat it as such and store for future use or freely pass to others threads.... but where it's guaranteed that no external agent can touch the inside of _foo.An intermediate type can reduce the chances of that happening, but it is not possible to prevent every potentially bad access in a system language like D. Ali
Aug 02 2012
On 02/08/12 10:25, Ali Çehreli wrote:.dup and .idup will always make a copy, std.conv.to won't:Brilliant. You could actually say that my _real_ problem was how to copy const(int)[] to int[] without the cast, as it's the cast that brings the lack of safety. std.conv.to seems to fit the bill perfectly.'const' is a better choice there: property const(int)[] foo() { return _foo; } You don't want to give the misleading promise that the data is immutable, as the caller may treat it as such and store for future use or freely pass to others threads.Very good point. It's too easy for me to forget this key difference between const and immutable.An intermediate type can reduce the chances of that happening, but it is not possible to prevent every potentially bad access in a system language like D.Sure. What I'm looking for is not some absolute and unachievable prohibition, but a safety-checking mechanism that would be flagged by the compiler. The real problem was that I couldn't just set bar = foo without bar being const, but didn't know how to do this without the cast, which violates safety. Since to!()() allows me to make that copy -- or is it copy-on-write? -- in a safe way, that seems a perfectly viable solution.
Aug 02 2012
If a function is marked safe, casting away immutable is not allowed by the compiler. But, you can't put this on the array. It needs to be on the user's functions: safe void foo(immutable(ubyte)[] arr) { auto cheating = cast(ubyte[]) arr; } test2.d(2): Error: cast from immutable(ubyte)[] to ubyte[] not allowed in safe code
Aug 02 2012
On Thursday, 2 August 2012 at 11:42:21 UTC, Joseph Rushton Wakeling wrote:Sure. What I'm looking for is not some absolute and unachievable prohibition, but a safety-checking mechanism that would be flagged by the compiler. The real problem was that I couldn't just set bar = foo without bar being const, but didn't know how to do this without the cast, which violates safety. Since to!()() allows me to make that copy -- or is it copy-on-write? -- in a safe way, that seems a perfectly viable solution.to!()() should make a copy from immutable to non, and vice-verse. COW can be easy to implement in functions, like string processing where once you've decided to change it, you make a mutable copy, modify it, then cast it back to immutable returning the new string. http://dlang.org/memory.html#copy-on-write COW outside of a function (as a type) could be a little different; a single flag could handle that case if it should copy or not; But having a structure to handle it may more complex. If there is not yet a COW container, I'll begin working on one.
Aug 02 2012