mutable, const, immutable guidelines

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Oct 16 12:12:25 PDT 2013


On Wed, Oct 16, 2013 at 08:52:22PM +0200, qznc wrote:
> On Wednesday, 16 October 2013 at 17:55:14 UTC, H. S. Teoh wrote:
> >Maybe it's helpful to understand how D's const system works. The
> >following diagram may help (please excuse the ASCII graphics):
> >
> >	       const
> >	      /     \
> >	mutable     immutable
> 
> I think people in this thread know how const works, but some think
> it is broken. Scenario is this:
> 
> Library code:
> 
> struct Foo { int x; }
> 
> User code:
> 
> Foo f;
> immutable f2 = f;
> 
> This works, even though the library writer might not have
> anticipated that someone makes Foo immutable. However, now the
> library writer obliviously releases a new version of the library,
> which extends it like this:
> 
> struct Foo {
>   int x;
>   private int[] history;
> }
> 
> Unfortunately, now the user code is broken due to the freshly
> introduced mutable aliasing. Personally, I think is fine. Upon
> compilation the user code gives a  error message and user developer
> can adapt to the code to the new library version. Some think the
> library writer should have a possibility to make this work.

Well, I think here the type system is working as advertised. Since the
original version of Foo has no mutable aliasing, implicit conversion to
immutable is OK (you're making a new binary copy of the data). But in
the second case, it will obviously violate immutability guarantees,
because in D, immutable is transitive, so immutable(Foo) in the second
case is equivalent to:

	struct Foo {
		int x;
		private immutable(int[]) history;
	}

You can't implicitly convert int[] to immutable(int[]) because whoever
holds the original int[] reference can use it to change the data, which
breaks the immutable guarantee of immutable(int[]).

The only way this could work is if you made a copy of the int[]. So the
library writer would have to provide a method for creating an immutable
copy of the struct (like an .idup method or something).

You may argue that this is bad because now user code is broken and you
need to rewrite it to use .idup, but I'd argue that relying on implicit
conversion to immutable already introduces a dependency on
implementation details of Foo, which should be avoided in the first
place if you want to have a clean encapsulation.

I mean, given a Foo type exported by a library, if that type is meant to
be an opaque type, then my code should make no assumptions about whether
it can implicitly convert to immutable. I should rather require the
library writer to provide an .idup method for creating an immutable
instance of the struct than to blindly write the code to implicitly
convert to immutable (thus introducing a hidden reliance on how Foo is
implemented) and then have it broken later when the library writer
changes its implementation.

OTOH, if it's not an opaque type, then it's no surprise that changing
its implementation should also require changing the code (that depends
on its implementation details).

So I don't see anything wrong with this particular scenario. What other
scenarios were being considered?


T

-- 
He who does not appreciate the beauty of language is not worthy to bemoan its flaws.


More information about the Digitalmars-d-learn mailing list