August 02, 2012 Re: Making array elements truly const/immutable | ||||
---|---|---|---|---|
| ||||
On Thursday, August 02, 2012 09:55:51 Joseph Rushton Wakeling wrote:
> 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.
const and immutable are the type system's protection against mutation. Casting them away removes that protection. D is a systems language and will let you do stuff like that. The only way to protect the underlying bytes is for them to be in ROM, because once you sidestep the type system, the language can't protect you anymore. Now, casting away const or immutable and mutating a variable is undefined behavior, so doing it is a really bad idea, but once the const or immutable is gone from the type, there's nothing that the type system can do to protect you, because you explicitly threw away its protection.
Anyone casting away immutable on the array you're returning from foo, is throwing away the type system's guarantees, and if they then mutate the variable, they're doing something that is undefined. So, if it blows up in their face, it's their fault. They're doing something stupid. There's no need for you to worry about protecting against it.
- Jonathan M Davis
|
Copyright © 1999-2021 by the D Language Foundation